├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── README.md │ ├── ci_kernel_test_helper.yml │ ├── ci_libkernel.yml │ ├── ci_libkerneldev.yml │ ├── ci_libmodule.yml │ ├── ci_libskynet.yml │ ├── ci_modules_kernel_test_suite.yml │ ├── ci_secure_download.yml │ ├── ci_secure_upload.yml │ ├── ci_webapps_kernel-test-suite.yml │ └── codeql-analysis.yml ├── .gitignore ├── LICENSE ├── README.md ├── extension ├── .eslintrc.json ├── .prettierignore ├── .prettierrc ├── README.md ├── assets │ ├── auth.html │ ├── icon.png │ ├── icon@2x.png │ └── manifest.json ├── clean.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src │ ├── background.ts │ ├── bootloader.ts │ └── bridge.ts └── tsconfig.json ├── http-server ├── README.md ├── go.mod └── main.go ├── kernel ├── .eslintrc.json ├── .module-salt ├── .prettierignore ├── .prettierrc ├── README.md ├── clean.js ├── module-skylink ├── package-lock.json ├── package.json ├── rollup.config.js ├── src-build │ └── build.ts ├── src │ ├── err.ts │ ├── index.ts │ ├── log.ts │ ├── logLargeState.ts │ ├── queries.ts │ ├── seed.ts │ └── version.ts ├── tsconfig.build.json └── tsconfig.json ├── libs ├── README.md ├── libkernel │ ├── .eslintrc.json │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── package-lock.json │ ├── package.json │ ├── src-test │ │ └── test.ts │ ├── src │ │ ├── auth.ts │ │ ├── index.ts │ │ ├── log.ts │ │ ├── messagedownload.ts │ │ ├── messageregistry.ts │ │ ├── messageupload.ts │ │ ├── messageversion.ts │ │ └── queries.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── libkmodule │ ├── .eslintrc.json │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── package-lock.json │ ├── package.json │ ├── src-test │ │ └── test.ts │ ├── src │ │ ├── independentFile.ts │ │ ├── index.ts │ │ ├── log.ts │ │ ├── messageDownload.ts │ │ ├── messageNoOp.ts │ │ ├── messageRegistry.ts │ │ ├── messageUpload.ts │ │ ├── messages.ts │ │ ├── queries.ts │ │ ├── seed.ts │ │ └── types.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── libskynet │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── benchmarks.ts │ ├── clean.js │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── spotTest.ts │ ├── src │ │ ├── apidefaultportals.ts │ │ ├── apidownloadskylink.ts │ │ ├── apidownloadverify.ts │ │ ├── apidownloadverifyresponse.ts │ │ ├── apiprogressivefetch.ts │ │ ├── apiregistryverify.ts │ │ ├── blake2b.ts │ │ ├── checkObjProps.ts │ │ ├── dictionary.ts │ │ ├── ed25519.test.ts │ │ ├── ed25519.ts │ │ ├── encoding.test.ts │ │ ├── encoding.ts │ │ ├── encrypt.ts │ │ ├── err.ts │ │ ├── errTracker.ts │ │ ├── filePrivate.test.ts │ │ ├── filePrivate.ts │ │ ├── index.ts │ │ ├── inode.ts │ │ ├── merkle.ts │ │ ├── objAsString.test.ts │ │ ├── objAsString.ts │ │ ├── parse.ts │ │ ├── registry.test.ts │ │ ├── registry.ts │ │ ├── seed.test.ts │ │ ├── seed.ts │ │ ├── sha512.ts │ │ ├── skylinkBitfield.test.ts │ │ ├── skylinkBitfield.ts │ │ ├── skylinkValidate.test.ts │ │ ├── skylinkValidate.ts │ │ ├── skylinkVerifyResolver.ts │ │ ├── stringifyJSON.test.ts │ │ ├── stringifyJSON.ts │ │ ├── types.ts │ │ ├── validateObjPropType.test.ts │ │ └── validateObjPropTypes.ts │ ├── tsconfig.json │ └── tsconfig.test.json └── libskynetnode │ ├── .eslintrc.json │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── package-lock.json │ ├── package.json │ ├── src-test │ └── test.ts │ ├── src │ ├── index.ts │ ├── progressivefetch.ts │ ├── registryread.ts │ ├── registrywrite.ts │ ├── seed.ts │ └── upload.ts │ ├── tsconfig.json │ └── tsconfig.test.json ├── modules ├── README.md ├── get-set-object │ ├── .eslintrc.json │ ├── .gitignore │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── kernel-test-helper │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── kernel-test-mysky │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── kernel-test-suite │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── secure-download │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── secure-registry │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── secure-upload │ ├── .eslintrc.json │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── skynet-portal │ ├── .eslintrc.json │ ├── .gitignore │ ├── .module-salt │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── module-skylink │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ │ └── build.ts │ ├── src │ │ ├── index.ts │ │ ├── init.ts │ │ ├── progressiveFetch.ts │ │ ├── registrySubscriptions.ts │ │ └── testPortalConnections.ts │ ├── tsconfig.build.json │ └── tsconfig.json └── template │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── clean.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── src-build │ └── build.ts │ ├── src │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json └── webapps ├── kernel-auth-page └── index.html ├── kernel-dev-dashboard ├── .gitignore ├── README.md ├── config │ ├── env.js │ ├── modules.js │ ├── paths.js │ ├── webpack.config.js │ ├── webpack │ │ └── persistentCache │ │ │ └── createEnvironmentHash.js │ └── webpackDevServer.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── index.html ├── scripts │ ├── build.js │ └── start.js ├── src │ ├── App.css │ ├── App.js │ ├── ExternalLink.js │ ├── Logo.js │ ├── ModuleOverrides.js │ ├── hooks │ │ └── useKernelQuery.js │ └── index.js └── tailwind.config.js └── kernel-test-suite ├── README.md ├── package-lock.json ├── package.json └── src └── pages ├── 404.js └── index.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @DavidVorick @kwypchlo @meeh0w 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Root level dependencies 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: monthly 8 | 9 | # /libs 10 | # - package-ecosystem: npm 11 | # directory: "/libs/libkernel" 12 | # schedule: 13 | # interval: monthly 14 | # open-pull-requests-limit: 10 15 | # - package-ecosystem: npm 16 | # directory: "/libs/libkerneldev" 17 | # schedule: 18 | # interval: monthly 19 | # open-pull-requests-limit: 10 20 | # - package-ecosystem: npm 21 | # directory: "/libs/libkmodule" 22 | # schedule: 23 | # interval: monthly 24 | # open-pull-requests-limit: 10 25 | # - package-ecosystem: npm 26 | # directory: "/libs/libskynet" 27 | # schedule: 28 | # interval: monthly 29 | # open-pull-requests-limit: 10 30 | 31 | # /modules 32 | # - package-ecosystem: npm 33 | # directory: "/modules/kernel-test-helper" 34 | # schedule: 35 | # interval: monthly 36 | # open-pull-requests-limit: 10 37 | # - package-ecosystem: npm 38 | # directory: "/modules/kernel-test-suite" 39 | # schedule: 40 | # interval: monthly 41 | # open-pull-requests-limit: 10 42 | # - package-ecosystem: npm 43 | # directory: "/modules/secure-download" 44 | # schedule: 45 | # interval: monthly 46 | # open-pull-requests-limit: 10 47 | # - package-ecosystem: npm 48 | # directory: "/modules/secure-registry" 49 | # schedule: 50 | # interval: monthly 51 | # open-pull-requests-limit: 10 52 | # - package-ecosystem: npm 53 | # directory: "/modules/secure-upload" 54 | # schedule: 55 | # interval: monthly 56 | # open-pull-requests-limit: 10 57 | 58 | # /webapps 59 | # - package-ecosystem: npm 60 | # directory: "/webapps/kernel-test-suite" 61 | # schedule: 62 | # interval: monthly 63 | # open-pull-requests-limit: 10 64 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # Github Workflows 2 | 3 | Current packages with no action coverage 4 | - extension 5 | - http-server 6 | - kernel 7 | - webapps/kernel-auth-page -------------------------------------------------------------------------------- /.github/workflows/ci_kernel_test_helper.yml: -------------------------------------------------------------------------------- 1 | name: "CI - kernel-test-helper" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - modules/kernel-test-helper/** 9 | 10 | defaults: 11 | run: 12 | working-directory: modules/kernel-test-helper 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_libkernel.yml: -------------------------------------------------------------------------------- 1 | name: "CI - libkernel" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - libs/libkernel/** 9 | 10 | defaults: 11 | run: 12 | working-directory: libs/libkernel 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run test 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_libkerneldev.yml: -------------------------------------------------------------------------------- 1 | name: "CI - libkerneldev" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - libs/libkerneldev/** 9 | 10 | defaults: 11 | run: 12 | working-directory: libs/libkerneldev 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run test 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_libmodule.yml: -------------------------------------------------------------------------------- 1 | name: "CI - libmodule" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - libs/libmodule/** 9 | 10 | defaults: 11 | run: 12 | working-directory: libs/libmodule 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run test 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_libskynet.yml: -------------------------------------------------------------------------------- 1 | name: "CI - libskynet" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - libs/libskynet/** 9 | 10 | defaults: 11 | run: 12 | working-directory: libs/libskynet 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run test 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_modules_kernel_test_suite.yml: -------------------------------------------------------------------------------- 1 | name: "CI - modules/kernel-test-suite" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - modules/kernel-test-suite/** 9 | 10 | defaults: 11 | run: 12 | working-directory: modules/kernel-test-suite 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_secure_download.yml: -------------------------------------------------------------------------------- 1 | name: "CI - secure-download" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - modules/secure-download/** 9 | 10 | defaults: 11 | run: 12 | working-directory: modules/secure-download 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_secure_upload.yml: -------------------------------------------------------------------------------- 1 | name: "CI - secure-upload" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - modules/secure-upload/** 9 | 10 | defaults: 11 | run: 12 | working-directory: modules/secure-upload 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/ci_webapps_kernel-test-suite.yml: -------------------------------------------------------------------------------- 1 | name: "CI - webapps/kernel-test-suite" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | paths: 8 | - webapps/kernel-test-suite/** 9 | 10 | defaults: 11 | run: 12 | working-directory: webapps/kernel-test-suite 13 | 14 | jobs: 15 | check: 16 | name: "Checks and Tests" 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [14.x, 16.x, 18.x] 23 | 24 | steps: 25 | - name: NPM Lint and Tests ${{ matrix.node-version }} 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-node@v3 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - run: npm run lint 31 | - run: npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [main] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [main] 20 | schedule: 21 | - cron: "0 20 * * 2" 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: ["javascript"] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dev tools 2 | *.swp 3 | notes 4 | out.txt 5 | 6 | # Folders created in the build process 7 | build 8 | build-cache 9 | bundle 10 | dist 11 | dist-test 12 | dist-build 13 | 14 | # http-server executable 15 | http-server/http-server 16 | 17 | # gatsby related files 18 | .cache 19 | node_modules 20 | public 21 | 22 | # ignore the build.js for secure-upload 23 | modules/secure-upload/build.js 24 | 25 | # Code Editors 26 | .vscode 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Skynet Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extension/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "prefer-const": "off", 16 | "@typescript-eslint/no-explicit-any": "off", 17 | "@typescript-eslint/no-non-null-assertion": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /extension/.prettierignore: -------------------------------------------------------------------------------- 1 | assets 2 | build 3 | dist 4 | node_modules 5 | -------------------------------------------------------------------------------- /extension/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /extension/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Extension 2 | 3 | This folder contains the source code for the full Skynet extension. The main 4 | purpose of the extension is to give users a fully trustless skynet experience. 5 | All assets that are loaded from Skynet get fully verfied before being loaded. 6 | This includes skapps, and includes the kernel itself. 7 | 8 | To the best of our knowledge, the full Skynet kernel is the only way to have a 9 | fully trustless browsing experience when using Skynet. 10 | 11 | NOTE: 'prefer-const' is off in eslint because we use 'eval' and we need some of 12 | the variables to be overwritten by the eval'd code. This means that the linter 13 | incorrectly believes that the variables are never modified. 14 | -------------------------------------------------------------------------------- /extension/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkynetLabs/skynet-kernel/ca7e46c06a96432dacdf061580cedc287d3a3dae/extension/assets/icon.png -------------------------------------------------------------------------------- /extension/assets/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkynetLabs/skynet-kernel/ca7e46c06a96432dacdf061580cedc287d3a3dae/extension/assets/icon@2x.png -------------------------------------------------------------------------------- /extension/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Adds a fully trustless skynet-kernel to your browser", 3 | "manifest_version": 2, 4 | "name": "skynet-kernel", 5 | "version": "0.1", 6 | "homepage_url": "https://skynetlabs.com/", 7 | "icons": { 8 | "48": "icon.png", 9 | "96": "icon@2x.png" 10 | }, 11 | 12 | // We need proxy permissions and so that we can enable fake 13 | // domains such as 'kernel.skynet' and 'marstorage.hns' and '1inch.eth' for the 14 | // user. We need webRequest and webRequestBlocking so we can inject the real 15 | // code into those fake urls. 16 | "permissions": ["proxy", "webRequest", "webRequestBlocking", ""], 17 | 18 | // The background script intercepts all requests to skt.us and replaces them 19 | // with trusted responses. 20 | "background": { 21 | "scripts": ["background.js"] 22 | }, 23 | 24 | // These content scripts perfectly match the pages 25 | // that are already being served by siasky.net, 26 | // we only inject them in the browser extension to 27 | // protect the user against siasky.net going rogue. 28 | "content_scripts": [ 29 | { 30 | "matches": ["http://kernel.skynet/"], 31 | "js": ["bootloader.js"], 32 | "run_at": "document_end", 33 | "all_frames": true 34 | }, 35 | { 36 | "matches": [""], 37 | "js": ["bridge.js"], 38 | "run_at": "document_end", 39 | "all_frames": true 40 | } 41 | ], 42 | 43 | // Give the content scripts access to a favicon for the skynet kernel. 44 | "web_accessible_resources": ["icon@2x.png", "auth.html"] 45 | } 46 | -------------------------------------------------------------------------------- /extension/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("build", { recursive: true, force: true }); 3 | fs.rmSync("dist", { recursive: true, force: true }); 4 | fs.rmSync("node_modules", { recursive: true, force: true }); 5 | -------------------------------------------------------------------------------- /extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skynet-web-extension", 3 | "version": "0.1.0", 4 | "description": "source code for the full skynet web extension", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "lint": "prettier -l 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json'", 9 | "clean": "node ./clean.js", 10 | "deps": "npm install libskynet@latest && npm audit fix", 11 | "update-deps": "npm run clean && npm run deps && npm install", 12 | "update-lint": "prettier -w 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json' --fix", 13 | "update": "npm run update-deps && npm run update-lint", 14 | "compile": "tsc && rollup -c", 15 | "build": "npm run clean && npm install && npm run lint && npm run compile && cpy \"assets/*\" dist" 16 | }, 17 | "author": "David Vorick", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "prettier": "^2.6.2", 26 | "rollup": "^2.75.6", 27 | "typescript": "^4.6.3" 28 | }, 29 | "dependencies": { 30 | "libskynet": "^0.1.7" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /extension/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | 3 | const background = { 4 | input: "build/background.js", 5 | output: { 6 | file: "dist/background.js", 7 | format: "cjs", 8 | }, 9 | plugins: [resolve()], 10 | }; 11 | 12 | const bootloader = { 13 | input: "build/bootloader.js", 14 | output: { 15 | file: "dist/bootloader.js", 16 | format: "cjs", 17 | }, 18 | plugins: [resolve()], 19 | }; 20 | 21 | const bridge = { 22 | input: "build/bridge.js", 23 | output: { 24 | file: "dist/bridge.js", 25 | format: "cjs", 26 | }, 27 | plugins: [resolve()], 28 | }; 29 | 30 | export default [background, bootloader, bridge]; 31 | -------------------------------------------------------------------------------- /extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./build" 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /http-server/README.md: -------------------------------------------------------------------------------- 1 | # http-server 2 | 3 | http-server is a very simple utility that runs an http server which responds to 4 | all requests that come to port 25252. The response is a simple text message, 5 | and is intended to be entirely irrelevant. When run beside the kernel, the 6 | kernel will go signficantly faster when loading crypto/web3 TLDs. It also 7 | provides some increased privacy. This utility is entirely optional, and we do 8 | not expect most users to run the utility. 9 | 10 | This utility helps the kernel overcome a limitation of the browser's 11 | onBeforeRequest API - even though web browser extensions are allowed to 12 | intercept and fully replace web requests, if the request went to a server that 13 | does not exist, the page will not load regardless of any logic in 14 | onBeforeRequest. 15 | 16 | We get around this in the kernel by setting up a proxy. Requests to imaginary 17 | domains such as 'kernel.skynet' get proxied to centralized servers such as 18 | 'siasky.net'. The web extension can't intercept the response until the response 19 | is provided from the server, which adds latency to loading imaginary domains. 20 | There's also a privacy component because which domain is being loaded will get 21 | leaked to that centralized server. This is a rather minimal amount of 22 | information to leak, but is still a privacy issue nonetheless. 23 | 24 | If the user is running a server locally on port 25252, the proxy request will 25 | instead use the local server. The result is that there is practically no 26 | latency in loading the imaginary domain, and also no data is getting leaked to 27 | a centralized server. 28 | 29 | No configuration is required to get this to work. As soon as the server is 30 | running in the background, the browser extension will start proxying to the 31 | local server. 32 | -------------------------------------------------------------------------------- /http-server/go.mod: -------------------------------------------------------------------------------- 1 | module http-server 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /http-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 10 | fmt.Fprintf(w, "simply proxy server") 11 | }) 12 | http.ListenAndServe(":25252", nil) 13 | } 14 | -------------------------------------------------------------------------------- /kernel/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "prefer-const": "off", 16 | "no-async-promise-executor": "off", 17 | "@typescript-eslint/no-explicit-any": "off", 18 | "@typescript-eslint/no-non-null-assertion": "off", 19 | "@typescript-eslint/no-unused-vars": "off" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /kernel/.module-salt: -------------------------------------------------------------------------------- 1 | dunes axes chrome boldly aloof alpine dime aztec asleep dummy ardent coal debut alerts yacht -------------------------------------------------------------------------------- /kernel/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | dist-build 4 | node_modules 5 | -------------------------------------------------------------------------------- /kernel/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /kernel/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Kernel Core 2 | 3 | This repo is the skynet kernel core. More info soon. 4 | 5 | Need to document somewhere (why not here) that the kernel+bootloader pairing 6 | currently depend on the idea that the kernel is minified and the bootloader is 7 | not. Otherwise the libraries that the kernel uses can conflict with the 8 | libraries that the bootloader uses and the eval can fail. 9 | -------------------------------------------------------------------------------- /kernel/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("build", { recursive: true, force: true }); 3 | fs.rmSync("dist", { recursive: true, force: true }); 4 | fs.rmSync("dist-build", { recursive: true, force: true }); 5 | fs.rmSync("node_modules", { recursive: true, force: true }); 6 | -------------------------------------------------------------------------------- /kernel/module-skylink: -------------------------------------------------------------------------------- 1 | AQBFjXpEBwbMwkBwYg0gdkeAM-yy9vlajfLtZSee9f-MDg -------------------------------------------------------------------------------- /kernel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skynet-kernel", 3 | "version": "0.2.0", 4 | "description": "source code for the full Skynet kernel", 5 | "author": "David Vorick", 6 | "license": "MIT", 7 | "type": "module", 8 | "main": "index.js", 9 | "scripts": { 10 | "lint": "prettier -l 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json'", 11 | "clean": "node ./clean.js", 12 | "deps": "npm install libskynet@latest && npm install libkmodule@latest && npm audit fix", 13 | "update-deps": "npm run clean && npm run deps && npm install", 14 | "update-lint": "prettier -w 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json' --fix", 15 | "update": "npm run update-deps && npm run update-lint && tsc", 16 | "build-script": "tsc --project tsconfig.build.json", 17 | "compile": "npm run build-script && tsc && rollup -c", 18 | "build": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js dev", 19 | "deploy": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js prod" 20 | }, 21 | "devDependencies": { 22 | "@rollup/plugin-node-resolve": "^13.3.0", 23 | "@types/read": "^0.0.29", 24 | "@typescript-eslint/eslint-plugin": "^5.18.0", 25 | "eslint": "^8.13.0", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.51", 35 | "libskynet": "^0.0.65" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kernel/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import { terser } from "rollup-plugin-terser"; 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | }; 12 | -------------------------------------------------------------------------------- /kernel/src/err.ts: -------------------------------------------------------------------------------- 1 | // notableErrors is a persistent list of errors that should be checked after 2 | // testing. You should only add to this array in the event of an error that 3 | // indicates a bug with the kernel. 4 | const notableErrors: string[] = []; 5 | 6 | // respondErr will send an error response to the caller that closes out the 7 | // query for the provided nonce. The extra inputs of 'messagePortal' and 8 | // 'isWorker' are necessary to handle the fact that the MessageEvent you get 9 | // from a worker message is different from the MessageEvent you get from a 10 | // window message, and also from the fact that postMessage has different 11 | // arguments depending on whether the messagePortal is a worker or a window. 12 | function respondErr(event: MessageEvent, messagePortal: any, isWorker: boolean, err: string) { 13 | const message = { 14 | nonce: event.data.nonce, 15 | method: "response", 16 | data: {}, 17 | err, 18 | }; 19 | if (isWorker === true) { 20 | messagePortal.postMessage(message); 21 | } else { 22 | messagePortal.postMessage(message, event.origin); 23 | } 24 | } 25 | 26 | export { notableErrors, respondErr }; 27 | -------------------------------------------------------------------------------- /kernel/src/log.ts: -------------------------------------------------------------------------------- 1 | import { objAsString } from "libskynet"; 2 | 3 | // wLog is a wrapper for the log and logErr functions, to deduplicate code. 4 | // 5 | // TODO: Need to implement a tag system for the logging. We will use the 6 | // dashboard to control logging messages and verbosity. 7 | function wLog(isErr: boolean, tag: string, ...inputs: any) { 8 | let message = "[skynet-kernel]\n" + tag; 9 | for (let i = 0; i < inputs.length; i++) { 10 | message += "\n"; 11 | message += objAsString(inputs[i]); 12 | } 13 | window.parent.postMessage( 14 | { 15 | method: "log", 16 | data: { 17 | isErr, 18 | message, 19 | }, 20 | }, 21 | "*" 22 | ); 23 | } 24 | function log(tag: string, ...inputs: any) { 25 | wLog(false, tag, ...inputs); 26 | } 27 | function logErr(tag: string, ...inputs: any) { 28 | wLog(true, tag, ...inputs); 29 | } 30 | 31 | export { log, logErr }; 32 | -------------------------------------------------------------------------------- /kernel/src/logLargeState.ts: -------------------------------------------------------------------------------- 1 | import { notableErrors } from "./err.js"; 2 | import { log } from "./log.js"; 3 | import { modules, modulesLoading, queries } from "./queries.js"; 4 | 5 | // Set up a loop that will periodically log all of the large objects in the 6 | // kernel, for the sake of making detection and debugging easier in the event 7 | // of a 8 | let waitTime = 30000; 9 | function logLargeObjects() { 10 | const queriesLenStr = Object.keys(queries).length.toString(); 11 | const modulesLenStr = Object.keys(modules).length.toString(); 12 | const modulesLoadingLenStr = Object.keys(modulesLoading).length.toString(); 13 | log( 14 | "open queries :: open modules :: modules loading :: notable errors : " + 15 | queriesLenStr + 16 | " :: " + 17 | modulesLenStr + 18 | " :: " + 19 | modulesLoadingLenStr + 20 | " :: " + 21 | notableErrors.length 22 | ); 23 | waitTime *= 1.25; 24 | setTimeout(logLargeObjects, waitTime); 25 | } 26 | setTimeout(logLargeObjects, waitTime); 27 | 28 | export { logLargeObjects }; 29 | -------------------------------------------------------------------------------- /kernel/src/seed.ts: -------------------------------------------------------------------------------- 1 | import { deriveMyskyRootKeypair, sha512 } from "libskynet"; 2 | 3 | // DEFAULT_MYSKY_ROOT_MODULES lists out the set of modules that are allowed to 4 | // receive the user's MySky root seed by default. 5 | const DEFAULT_MYSKY_ROOT_MODULES = [ 6 | "AQBmFdF14nfEQrERIknEBvZoTXxyxG8nejSjH6ebCqcFkQ", // Resolver link for Redsolver's Mysky Module 7 | "IABOv7_dkJwtuaFBeB6eTR32mSvtLsBRVffEY9yYL0v0rA", // Immutable link for the mysky test module 8 | ]; 9 | 10 | // This variable is the seed that got loaded into memory by the bootloader, and 11 | // is the user seed. We keep this seed in memory, because if the user ever logs 12 | // out the kernel is expected to refresh, which will clear the seed. 13 | declare let userSeed: Uint8Array; 14 | 15 | // Derive the active seed for this session. We define an active seed so that 16 | // the user has control over changing accounts later, they can "change 17 | // accounts" by switching up their active seed and then reloading all modules. 18 | // 19 | // NOTE: If we ever add functionality to change the active seed (which would be 20 | // equivalent to the user switching accounts), we need to make sure that the 21 | // myskyRootKeypair is no longer being derived from the userSeed, but rather 22 | // changes its derivation to the new activeSeed. We only want to use the 23 | // userSeed as the root for the myskyRootKeypair if the active seed is the 24 | // "defaultUserActiveSeed". 25 | const activeSeedSalt = new TextEncoder().encode("defaultUserActiveSeed"); 26 | const activeSeedPreimage = new Uint8Array(userSeed.length + activeSeedSalt.length); 27 | activeSeedPreimage.set(userSeed, 0); 28 | activeSeedPreimage.set(activeSeedSalt, userSeed.length); 29 | const activeSeed = sha512(activeSeedPreimage).slice(0, 16); 30 | const myskyRootKeypair = deriveMyskyRootKeypair(userSeed); 31 | 32 | export { DEFAULT_MYSKY_ROOT_MODULES, activeSeed, myskyRootKeypair }; 33 | -------------------------------------------------------------------------------- /kernel/src/version.ts: -------------------------------------------------------------------------------- 1 | // Set the distribution and version of this kernel. There may be other versions 2 | // of the kernel in the world produced by other development teams, so openly 3 | // declaring the version number and development team allows other pieces of 4 | // software to determine what features are or are not supported. 5 | // 6 | // At some point we may want something like a capabilities array, but the 7 | // ecosystem isn't mature enough to need that. 8 | const KERNEL_DISTRO = "SkynetLabs"; 9 | const KERNEL_VERSION = "0.9.1"; 10 | 11 | export { KERNEL_DISTRO, KERNEL_VERSION }; 12 | -------------------------------------------------------------------------------- /kernel/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /kernel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /libs/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Kernel Libs 2 | 3 | This folder contains libraries that are useful for working with and developing 4 | the Skynet kernel. 5 | 6 | ##### libkernel 7 | 8 | libkernel is a library that is intended to be used by skapps to interact with 9 | the skynet kernel. It is a minimal library with not too many functions, but 10 | provides all core functionality for basic Skynet interactions. 11 | 12 | ##### libkmodule 13 | 14 | libkmodule is a library that is intended to be used by kernel modules. It is a 15 | minimal library that provides all core functionality for basic Skynet and 16 | kernel interactions, and for all basic module behaviors. 17 | 18 | ##### libskynet 19 | 20 | libskynet is a library for people working with the actual Skynet protocol. Most 21 | developers will not need to use libskynet, but it is used heavily thorughout 22 | the kernel itself, and in the core skynet modules. 23 | 24 | NOTE: There is discussion around breaking libskynet into libskynet and 25 | libskynetapi, with the libskynetapi functions being all of the functions that 26 | perform actual network calls. It is not certain yet whether this is a desirable 27 | spit. If the api calls are pulled out into their own library, libskynet becomes 28 | fully isomorphic. 29 | 30 | ##### libskynetnode 31 | 32 | libskynetnode is a library that implements skynet API calls using nodejs 33 | libraries. There is no isomorphic support for the 'fetch' call, and we elected 34 | to break the node functions out into their own library rather than use 35 | polyfills. Most of the functionality of libskynetnode comes from libskynet, as 36 | most functions in libskynet are isomorphic. 37 | 38 | ## Code Conventions 39 | 40 | All libraries should be written in typescript. 41 | 42 | Consts should always be all caps with underscores: `EXAMPLE_CONST` 43 | 44 | Types should always be prefixed by `t_` 45 | 46 | Functions should not throw under any circumstances. All potential throws should 47 | be caught inside of the function, and an error should be returned instead. 48 | 49 | Promises should always resolve. If the promise can experience an error, then 50 | the promise should resolve with an error type that can be checked. 51 | 52 | Things named 'skylink' should indicate their type in the name. This convention 53 | is because we don't have much structure around when and where we skylinks in 54 | each format, and we want to make sure we track their types well. 55 | + use 'skylink32' for base32 skylinks formatted as strings (uncommon) 56 | + use 'skylink64' for base64 skylinks formatted as strings (common) 57 | + use 'skylinkU8' for Uint8Array skylinks (common) 58 | -------------------------------------------------------------------------------- /libs/libkernel/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-empty-function": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/libkernel/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-test 3 | node_modules 4 | -------------------------------------------------------------------------------- /libs/libkernel/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /libs/libkernel/README.md: -------------------------------------------------------------------------------- 1 | # libkernel 2 | 3 | libkernel is a node module for web developers what would like to use Skynet in 4 | their applications. It creates a direct connection to the Skynet kernel and 5 | then provides helper methods for interacting with the kernel. 6 | 7 | A quirk of libkernel is that errors are of the type `string | null` rather than 8 | being type `Error`. This is because libkernel is frequently used in webworkers 9 | and other settings where errors need to be sent over postmessage, and the 10 | `Error` type is not suitable for postmessage. 11 | 12 | libkernel is still being reviewed, but is expected to be stable. There may be a 13 | small number of breaking changes in the near future, and after that we will be 14 | providing strong compatibility promises around the libkernel API. 15 | 16 | ## Roadmap 17 | 18 | libkernel needs to expose a method to directly send messages to the kernel, 19 | without adding any formatting or wrapping. This is necessary in particular for 20 | the test suite so that the test suite can attempt to send malformed messages 21 | and ensure that the kernel is performing all of the safety checks on bad 22 | messages and continues to function after receiving malformed messages. 23 | -------------------------------------------------------------------------------- /libs/libkernel/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("dist", { recursive: true, force: true }); 3 | fs.rmSync("dist-test", { recursive: true, force: true }); 4 | fs.rmSync("node_modules", { recursive: true, force: true }); 5 | -------------------------------------------------------------------------------- /libs/libkernel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libkernel", 3 | "version": "0.1.48", 4 | "author": "Skynet Labs", 5 | "description": "helper library to interact with skynet and the skynet kernel", 6 | "main": "dist/index.js", 7 | "type": "module", 8 | "types": "dist/index.d.js", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "files": [ 13 | "/dist" 14 | ], 15 | "scripts": { 16 | "lint": "prettier -l 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json'", 17 | "clean": "node ./clean.js", 18 | "deps": "npm install libskynet@latest && npm audit fix", 19 | "update-deps": "npm run clean && npm run deps && npm install", 20 | "update-lint": "prettier -w 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json' --fix", 21 | "update": "npm run update-deps && npm run update-lint", 22 | "test": "tsc && tsc --project tsconfig.test.json && node ./dist-test/test.js", 23 | "build": "npm run clean && npm install && npm run lint && tsc", 24 | "prepublishOnly": "npm run clean && npm install && npm run lint && npm run test && npm run build" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^17.0.23", 28 | "@typescript-eslint/eslint-plugin": "^5.19.0", 29 | "eslint": "^8.13.0", 30 | "prettier": "^2.6.2" 31 | }, 32 | "dependencies": { 33 | "@skynetlabs/libskynet": "^0.0.48", 34 | "libskynet": "^0.0.64" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/libkernel/src-test/test.ts: -------------------------------------------------------------------------------- 1 | console.log("tests have passed"); 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /libs/libkernel/src/auth.ts: -------------------------------------------------------------------------------- 1 | import { init, kernelAuthLocation, kernelLoadedPromise, loginPromise, logoutPromise } from "./queries.js"; 2 | import { Err } from "libskynet"; 3 | 4 | // There are 5 stages of auth. 5 | // 6 | // Stage 0: Bootloader is not loaded. 7 | // Stage 1: Bootloader is loaded, user is not logged in. 8 | // Stage 2: Bootloader is loaded, user is logged in. 9 | // Stage 3: Kernel is loaded, user is logged in. 10 | // Stage 4: Kernel is loaded, user is logged out. 11 | // 12 | // init() will block until auth has reached stage 1. If the user is already 13 | // logged in from a previous session, auth will immediately progress to stage 14 | // 2. 15 | // 16 | // loginComplete() will block until auth has reached stage 2. The kernel is not 17 | // ready to receive messages yet, but apps do not need to present users with a 18 | // login dialog. 19 | // 20 | // kernelLoaded() will block until auth has reached stage 3. kernelLoaded() 21 | // returns a promise that can resolve with an error. If there was an error, it 22 | // means the kernel could not be loaded and cannot be used. 23 | // 24 | // logoutComplete() will block until auth has reached stage 4. libkernel does 25 | // not support resetting the auth stages, once stage 4 has been reached the app 26 | // needs to refresh. 27 | 28 | // loginComplete will resolve when the user has successfully logged in. 29 | function loginComplete(): Promise { 30 | return loginPromise; 31 | } 32 | 33 | // kernelLoaded will resolve when the user has successfully loaded the kernel. 34 | // If there was an error in loading the kernel, the error will be returned. 35 | // 36 | // NOTE: kernelLoaded will not resolve until after loginComplete has resolved. 37 | function kernelLoaded(): Promise { 38 | return kernelLoadedPromise; 39 | } 40 | 41 | // logoutComplete will resolve when the user has logged out. Note that 42 | // logoutComplete will only resolve if the user logged in first - if the user 43 | // was not logged in to begin with, this promise will not resolve. 44 | function logoutComplete(): Promise { 45 | return logoutPromise; 46 | } 47 | 48 | // openAuthWindow is intended to be used as an onclick target when the user 49 | // clicks the 'login' button on a skynet application. It will block until the 50 | // auth location is known, and then it will pop open the correct auth window 51 | // for the user. 52 | // 53 | // NOTE: openAuthWindow will only open a window if the user is not already 54 | // logged in. If the user is already logged in, this function is a no-op. 55 | // 56 | // NOTE: When using this function, you probably want to have your login button 57 | // faded out or presenting the user with a spinner until init() resolves. In 58 | // the worst case (user has no browser extension, and is on a slow internet 59 | // connection) this could take multiple seconds. 60 | function openAuthWindow(): void { 61 | // openAuthWindow doesn't care what the auth status is, it's just trying to 62 | // open the right window. 63 | init().then(() => { 64 | window.open(kernelAuthLocation, "_blank"); 65 | }); 66 | } 67 | 68 | export { loginComplete, kernelLoaded, logoutComplete, openAuthWindow }; 69 | -------------------------------------------------------------------------------- /libs/libkernel/src/index.ts: -------------------------------------------------------------------------------- 1 | export { kernelLoaded, loginComplete, logoutComplete, openAuthWindow } from "./auth.js"; 2 | export { download } from "./messagedownload.js"; 3 | export { registryRead, registryWrite } from "./messageregistry.js"; 4 | export { upload } from "./messageupload.js"; 5 | export { kernelVersion } from "./messageversion.js"; 6 | export { callModule, connectModule, init, newKernelQuery } from "./queries.js"; 7 | export { addContextToErr, checkObj, objAsString } from "libskynet"; 8 | -------------------------------------------------------------------------------- /libs/libkernel/src/log.ts: -------------------------------------------------------------------------------- 1 | // log provides a wrapper for console.log that prefixes '[libkernel]' to the 2 | // output. 3 | function log(...inputs: any) { 4 | console.log("[libkernel]", ...inputs); 5 | } 6 | 7 | // logErr provides a wrapper for console.error that prefixes '[libkernel]' to 8 | // the output. 9 | function logErr(...inputs: any) { 10 | console.error("[libkernel]", ...inputs); 11 | } 12 | 13 | export { log, logErr }; 14 | -------------------------------------------------------------------------------- /libs/libkernel/src/messagedownload.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { addContextToErr, Err } from "libskynet"; 3 | 4 | // download will take a skylink and return the file data for that skylink. The 5 | // download occurs using a kernel module that verifies the data's integrity and 6 | // prevents the portal from lying about the download. 7 | function download(skylink: string): Promise<[Uint8Array, Err]> { 8 | return new Promise((resolve) => { 9 | const downloadModule = "AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw"; 10 | const data = { 11 | skylink, 12 | }; 13 | callModule(downloadModule, "secureDownload", data).then(([result, err]) => { 14 | // Pull the fileData out of the result. 15 | if (err !== null) { 16 | resolve([new Uint8Array(0), addContextToErr(err, "unable to complete download")]); 17 | return; 18 | } 19 | resolve([result.fileData, null]); 20 | }); 21 | }); 22 | } 23 | 24 | export { download }; 25 | -------------------------------------------------------------------------------- /libs/libkernel/src/messageregistry.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { Ed25519Keypair, Err, addContextToErr } from "libskynet"; 3 | 4 | interface registryReadResult { 5 | exists: boolean; 6 | entryData?: Uint8Array; 7 | revision?: bigint; 8 | } 9 | 10 | // registryRead will perform a registry read on a portal. readEntry does not 11 | // guarantee that the latest revision has been provided, however it does 12 | // guarantee that the provided data has a matching signature. 13 | // 14 | // registryRead returns the full registry entry object provided by the module 15 | // because the object is relatively complex and all of the fields are more or 16 | // less required. 17 | function registryRead(publicKey: Uint8Array, dataKey: Uint8Array): Promise<[registryReadResult, Err]> { 18 | return new Promise((resolve) => { 19 | const registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; 20 | const data = { 21 | publicKey, 22 | dataKey, 23 | }; 24 | callModule(registryModule, "readEntry", data).then(([result, err]) => { 25 | if (err !== null) { 26 | resolve([{} as any, addContextToErr(err, "readEntry module call failed")]); 27 | return; 28 | } 29 | resolve([ 30 | { 31 | exists: result.exists, 32 | entryData: result.entryData, 33 | revision: result.revision, 34 | }, 35 | null, 36 | ]); 37 | }); 38 | }); 39 | } 40 | 41 | // registryWrite will perform a registry write on a portal. 42 | // 43 | // registryWrite is not considered a safe function, there are easy ways to 44 | // misuse registryWrite such that user data will be lost. We recommend using a 45 | // safe set of functions for writing to the registry such as getsetjson. 46 | function registryWrite( 47 | keypair: Ed25519Keypair, 48 | dataKey: Uint8Array, 49 | entryData: Uint8Array, 50 | revision: BigInt 51 | ): Promise<[string, Err]> { 52 | return new Promise((resolve) => { 53 | const registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; 54 | const callData = { 55 | publicKey: keypair.publicKey, 56 | secretKey: keypair.secretKey, 57 | dataKey, 58 | entryData, 59 | revision, 60 | }; 61 | callModule(registryModule, "writeEntry", callData).then(([result, err]) => { 62 | if (err !== null) { 63 | resolve(["", err]); 64 | return; 65 | } 66 | resolve([result.entryID, null]); 67 | }); 68 | }); 69 | } 70 | 71 | export { registryRead, registryWrite }; 72 | -------------------------------------------------------------------------------- /libs/libkernel/src/messageupload.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { addContextToErr, Err } from "libskynet"; 3 | 4 | // upload will take a filename and some file data and perform a secure upload 5 | // to Skynet. All data is verified and the correct Skylink is returned. This 6 | // function cannot fully guarantee that the data was pinned, but it can fully 7 | // guarantee that the final skylink matches the data that was presented for the 8 | // upload. 9 | function upload(filename: string, fileData: Uint8Array): Promise<[string, Err]> { 10 | return new Promise((resolve) => { 11 | // Prepare the module call. 12 | const uploadModule = "AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg"; 13 | const data = { 14 | filename, 15 | fileData, 16 | }; 17 | callModule(uploadModule, "secureUpload", data).then(([result, err]) => { 18 | // Pull the skylink out of the result. 19 | if (err !== null) { 20 | resolve(["", addContextToErr(err, "uable to complete upload")]); 21 | return; 22 | } 23 | resolve([result.skylink, null]); 24 | }); 25 | }); 26 | } 27 | 28 | export { upload }; 29 | -------------------------------------------------------------------------------- /libs/libkernel/src/messageversion.ts: -------------------------------------------------------------------------------- 1 | import { newKernelQuery } from "./queries.js"; 2 | import { Err } from "libskynet"; 3 | 4 | // kernelVersion will fetch the version number of the kernel. If successful, 5 | // the returned value will be an object containing a field 'version' with a 6 | // version string, and a 'distribtion' field with a string that states the 7 | // distribution of the kernel". 8 | function kernelVersion(): Promise<[string, string, Err]> { 9 | return new Promise((resolve) => { 10 | const [, query] = newKernelQuery("version", {}, false); 11 | query.then(([result, err]) => { 12 | if (err !== null) { 13 | resolve(["", "", err]); 14 | return; 15 | } 16 | resolve([result.version, result.distribution, err]); 17 | }); 18 | }); 19 | } 20 | 21 | export { kernelVersion }; 22 | -------------------------------------------------------------------------------- /libs/libkernel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./dist" 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/libkernel/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "module": "esnext", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "outDir": "./dist-test" 10 | }, 11 | "include": ["src-test/**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /libs/libkmodule/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-empty-function": "off", 18 | "no-async-promise-executor": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /libs/libkmodule/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-test 3 | node_modules 4 | -------------------------------------------------------------------------------- /libs/libkmodule/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /libs/libkmodule/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("dist", { recursive: true, force: true }); 3 | fs.rmSync("dist-test", { recursive: true, force: true }); 4 | fs.rmSync("node_modules", { recursive: true, force: true }); 5 | -------------------------------------------------------------------------------- /libs/libkmodule/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libkmodule", 3 | "version": "0.2.53", 4 | "author": "Skynet Labs", 5 | "description": "helper library to interact with the skynet kernel from a module", 6 | "main": "dist/index.js", 7 | "type": "module", 8 | "types": "dist/index.d.js", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "files": [ 13 | "/dist" 14 | ], 15 | "scripts": { 16 | "lint": "prettier -l 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json'", 17 | "clean": "node ./clean.js", 18 | "deps": "npm install libskynet@latest && npm audit fix", 19 | "update-deps": "npm run clean && npm run deps && npm install", 20 | "update-lint": "prettier -w 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json' --fix", 21 | "update": "npm run update-deps && npm run update-lint", 22 | "test": "tsc && tsc --project tsconfig.test.json && node ./dist-test/test.js", 23 | "build": "npm run clean && npm install && npm run lint && tsc", 24 | "prepublishOnly": "npm run clean && npm install && npm run lint && npm run test && npm run build" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^17.0.23", 28 | "@typescript-eslint/eslint-plugin": "^5.19.0", 29 | "eslint": "^8.13.0", 30 | "prettier": "^2.6.2" 31 | }, 32 | "dependencies": { 33 | "libskynet": "^0.1.9" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /libs/libkmodule/src-test/test.ts: -------------------------------------------------------------------------------- 1 | console.log("tests have passed"); 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /libs/libkmodule/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ERR_EXISTS, 3 | ERR_NOT_EXISTS, 4 | createIndependentFileSmall, 5 | openIndependentFileSmall, 6 | viewIndependentFileSmall, 7 | } from "./independentFile.js"; 8 | export { log, logErr } from "./log.js"; 9 | export { download } from "./messageDownload.js"; 10 | export { RegistryReadResult, registryRead, registryWrite } from "./messageRegistry.js"; 11 | export { ActiveQuery, addHandler, handleMessage } from "./messages.js"; 12 | export { upload } from "./messageUpload.js"; 13 | export { callModule, connectModule, newKernelQuery } from "./queries.js"; 14 | export { getDataFromKernel, getSeed } from "./seed.js"; 15 | export { moduleQuery, presentSeedData } from "./types.js"; 16 | export { DataFn, Err, addContextToErr, objAsString, validateObjPropTypes } from "libskynet"; 17 | -------------------------------------------------------------------------------- /libs/libkmodule/src/log.ts: -------------------------------------------------------------------------------- 1 | import { objAsString } from "libskynet"; 2 | 3 | // logHelper is a helper function that runs the code for both log and logErr. 4 | // It takes a boolean indiciating whether the log should be an error, and then 5 | // it stringifies all of the reamining inputs and sends them to the kernel in a 6 | // log message. 7 | function logHelper(isErr: boolean, ...inputs: any) { 8 | let message = ""; 9 | for (let i = 0; i < inputs.length; i++) { 10 | if (i !== 0) { 11 | message += "\n"; 12 | } 13 | message += objAsString(inputs[i]); 14 | } 15 | postMessage({ 16 | method: "log", 17 | data: { 18 | isErr, 19 | message, 20 | }, 21 | }); 22 | } 23 | 24 | // log is a helper function to send a bunch of inputs to the kernel serialized 25 | // as a log message. Note that any inputs which cannot be stringified using 26 | // JSON.stringify will be substituted with a placeholder string indicating that 27 | // the input could not be stringified. 28 | function log(...inputs: any) { 29 | console.log(...inputs); 30 | logHelper(false, ...inputs); 31 | } 32 | 33 | // logErr is a helper function to send a bunch of inputs to the kernel 34 | // serialized as an error log message. Note that any inputs which cannot be 35 | // stringified using JSON.stringify will be substituted with a placeholder 36 | // string indicating that the input could not be stringified. 37 | function logErr(...inputs: any) { 38 | console.error(...inputs); 39 | logHelper(true, ...inputs); 40 | } 41 | 42 | export { log, logErr }; 43 | -------------------------------------------------------------------------------- /libs/libkmodule/src/messageDownload.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { addContextToErr, Err } from "libskynet"; 3 | 4 | // download will take a skylink and return the file data for that skylink. The 5 | // download occurs using a kernel module that verifies the data's integrity and 6 | // prevents the portal from lying about the download. 7 | function download(skylink: string): Promise<[fileData: Uint8Array, err: Err]> { 8 | return new Promise((resolve) => { 9 | // Construct the module call. 10 | const downloadModule = "AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw"; 11 | const data = { 12 | skylink, 13 | }; 14 | 15 | // Perform the module call and extract the fileData from the result. 16 | callModule(downloadModule, "secureDownload", data).then(([result, err]) => { 17 | if (err !== null) { 18 | resolve([new Uint8Array(0), addContextToErr(err, "secureDownload module call failed")]); 19 | return; 20 | } 21 | resolve([result.fileData, null]); 22 | }); 23 | }); 24 | } 25 | 26 | export { download }; 27 | -------------------------------------------------------------------------------- /libs/libkmodule/src/messageNoOp.ts: -------------------------------------------------------------------------------- 1 | import { ActiveQuery } from "./messages.js"; 2 | 3 | // handleNoOp create a no-op function for the module that allows the module to 4 | // be "warmed up", meaning the kernel will stick the module into the cache so 5 | // that it loads faster when a user actually needs the module. 6 | function handleNoOp(aq: ActiveQuery) { 7 | aq.respond({ success: true }); 8 | } 9 | 10 | export { handleNoOp }; 11 | -------------------------------------------------------------------------------- /libs/libkmodule/src/messageRegistry.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { Ed25519Keypair, Err, addContextToErr } from "libskynet"; 3 | 4 | // RegistryReadResult establishes the return type for a registry read performed 5 | // on the registry module. 6 | interface RegistryReadResult { 7 | exists: boolean; 8 | deleted?: boolean; 9 | entryData?: Uint8Array; 10 | revision?: bigint; 11 | } 12 | 13 | // registryRead will perform a registry read on a portal. readEntry does not 14 | // guarantee that the latest revision has been provided, however it does 15 | // guarantee that the provided data has a matching signature. 16 | function registryRead(publicKey: Uint8Array, dataKey: Uint8Array): Promise<[RegistryReadResult, Err]> { 17 | return new Promise((resolve) => { 18 | // Build the module call. 19 | const registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; 20 | const data = { 21 | publicKey, 22 | dataKey, 23 | }; 24 | 25 | // Perform the module call and extract the data from the result. 26 | callModule(registryModule, "readEntry", data).then(([result, err]) => { 27 | if (err !== null) { 28 | resolve([{} as any, addContextToErr(err, "readEntry module call failed")]); 29 | return; 30 | } 31 | resolve([ 32 | { 33 | exists: result.exists, 34 | entryData: result.entryData, 35 | revision: result.revision, 36 | }, 37 | null, 38 | ]); 39 | }); 40 | }); 41 | } 42 | 43 | // registryWrite will perform a registry write on a portal. 44 | // 45 | // registryWrite is not considered a safe function, there are easy ways to 46 | // misuse registryWrite such that user data will be lost. We recommend using a 47 | // safe set of functions for writing to the registry such as getsetjson. 48 | function registryWrite( 49 | keypair: Ed25519Keypair, 50 | dataKey: Uint8Array, 51 | entryData: Uint8Array, 52 | revision: BigInt 53 | ): Promise<[entryID: Uint8Array, err: Err]> { 54 | return new Promise((resolve) => { 55 | // Build the module call. 56 | const registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; 57 | const callData = { 58 | publicKey: keypair.publicKey, 59 | secretKey: keypair.secretKey, 60 | dataKey, 61 | entryData, 62 | revision, 63 | }; 64 | 65 | // Call the module and extract the entryID. 66 | callModule(registryModule, "writeEntry", callData).then(([result, err]) => { 67 | if (err !== null) { 68 | resolve([new Uint8Array(0), addContextToErr(err, "writeEntry module call failed")]); 69 | return; 70 | } 71 | resolve([result.entryID, null]); 72 | }); 73 | }); 74 | } 75 | 76 | export { registryRead, registryWrite, RegistryReadResult }; 77 | -------------------------------------------------------------------------------- /libs/libkmodule/src/messageUpload.ts: -------------------------------------------------------------------------------- 1 | import { callModule } from "./queries.js"; 2 | import { Err, addContextToErr } from "libskynet"; 3 | 4 | // upload will take a filename and some file data and perform a secure upload 5 | // to Skynet. All data is verified and the correct Skylink is returned. This 6 | // function cannot fully guarantee that the data was pinned, but it can fully 7 | // guarantee that the final skylink matches the data that was presented for the 8 | // upload. 9 | // 10 | // The return value is a skylink and an error. 11 | function upload(filename: string, fileData: Uint8Array): Promise<[skylink: string, err: Err]> { 12 | return new Promise((resolve) => { 13 | // Build the module call. 14 | const uploadModule = "AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg"; 15 | const data = { 16 | filename, 17 | fileData, 18 | }; 19 | 20 | // Perform the module call and extract the skylink from the result. 21 | callModule(uploadModule, "secureUpload", data).then(([result, err]) => { 22 | if (err !== null) { 23 | resolve(["", addContextToErr(err, "secureUpload module call failed")]); 24 | return; 25 | } 26 | resolve([result.skylink, null]); 27 | }); 28 | }); 29 | } 30 | 31 | export { upload }; 32 | -------------------------------------------------------------------------------- /libs/libkmodule/src/seed.ts: -------------------------------------------------------------------------------- 1 | import { ActiveQuery, DataFn } from "./messages.js"; 2 | 3 | // Define a set of helper variables that track whether the seed has been 4 | // received by the kernel yet. 5 | let resolveSeed: DataFn; 6 | const seedPromise: Promise = new Promise((resolve) => { 7 | resolveSeed = resolve; 8 | }); 9 | 10 | // dataFromKernel will hold any data that is sent by the kernel in the 11 | // 'presentSeed' call that happens at startup. 12 | // 13 | // dataFromKernel should not be accessed until 'seedPromise' has been resolved. 14 | let dataFromKernel: any; 15 | 16 | // getSeed will return a promise that resolves when the seed is available. 17 | function getSeed(): Promise { 18 | return seedPromise; 19 | } 20 | 21 | // getDataFromKernel will resolve with the data that was provided by the kernel 22 | // in 'presentSeed' once that data is available. 23 | function getDataFromKernel(): Promise { 24 | return new Promise((resolve) => { 25 | seedPromise.then(() => { 26 | resolve(dataFromKernel); 27 | }); 28 | }); 29 | } 30 | 31 | // handlePresentSeed will accept a seed from the kernel and unblock any method 32 | // that is waiting for the seed. 33 | function handlePresentSeed(aq: ActiveQuery) { 34 | dataFromKernel = aq.callerInput; 35 | resolveSeed(aq.callerInput.seed); 36 | } 37 | 38 | export { getDataFromKernel, getSeed, handlePresentSeed }; 39 | -------------------------------------------------------------------------------- /libs/libkmodule/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Ed25519Keypair } from "libskynet"; 2 | 3 | // moduleQuery defines a query that can be sent to a module. The method is used 4 | // to tell the module what query is being made. The domain is set by the 5 | // kernel, and is guaranteed to match the domain of the caller. The module can 6 | // use the 'domain' to enforce access control policies. The 'data' can be any 7 | // arbitrary object, and will depend on the method. The module developer is 8 | // ultimately the one who decides what data should be provided as input to each 9 | // method call. 10 | // 11 | // NOTE: While the kerenl does do verification for the method and domain, the 12 | // kernel does not do any verification for the data field. The module itself is 13 | // responsible for verifying all inputs provided in the data field. 14 | interface moduleQuery { 15 | method: string; 16 | domain: string; 17 | data: any; 18 | } 19 | 20 | // presentSeedData contains the data that gets sent in a 'presentSeed' call 21 | // from the kernel. 'presentSeed' is called on the module immediately after the 22 | // module starts up. 23 | // 24 | // The 'seed' is a unique seed dervied by the kernel for the module based on 25 | // the module's domain and the seed of the user. Modules in different domains 26 | // will have different seeds, and have no way to guess what the seeds of other 27 | // modules are. 28 | // 29 | // It is safe to use the 'seed' for things like blockchain wallets. 30 | // 31 | // If the module has been given access to the mysky root keypair, 32 | // presentSeedData will include the myskyRootKeypair. If the module does not 33 | // have access to the mysky root keypair, the field will not be included. A 34 | // module that receives the mysky root keypair has full read and write access 35 | // to all of the user's public and private mysky files. 36 | // 37 | // NOTE: Using mysky for private files is considered deprecated. 38 | interface presentSeedData { 39 | seed: Uint8Array; 40 | myskyRootKeypair?: Ed25519Keypair; 41 | } 42 | 43 | export { moduleQuery, presentSeedData }; 44 | -------------------------------------------------------------------------------- /libs/libkmodule/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./dist" 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/libkmodule/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "outDir": "./dist-test" 11 | }, 12 | "include": ["src-test/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /libs/libskynet/.eslintignore: -------------------------------------------------------------------------------- 1 | src/parse.ts 2 | -------------------------------------------------------------------------------- /libs/libskynet/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/libskynet/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-test 3 | node_modules 4 | -------------------------------------------------------------------------------- /libs/libskynet/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /libs/libskynet/README.md: -------------------------------------------------------------------------------- 1 | # libskynet 2 | 3 | libskynet is a node module targeted at low level skynet developers. libskynet 4 | provides helper methods that make it easier to manipulate base skynet 5 | primitives like skylinks and to work with things like download and registry 6 | proofs that are sent by the portal. 7 | 8 | Most developers building applications on Skynet should use libkernel instead of 9 | libskynet. 10 | 11 | One quirk of libskynet is that errors always use the type `string | null` 12 | rather than being type `Error`. This is because the errors often need to be 13 | sent over postmessage, and the `Error` type cannot be sent over postmessage. 14 | 15 | libskynet is still unstable. We are trying to minimize the total number of 16 | breaking changes and expect to be able to offer strong compatibility changes 17 | soon. 18 | -------------------------------------------------------------------------------- /libs/libskynet/benchmarks.ts: -------------------------------------------------------------------------------- 1 | import { decryptFileSmall, encryptFileSmall } from "./src/filePrivate.js" 2 | import { generateSeedPhraseDeterministic, seedPhraseToSeed } from "./src/seed.js" 3 | 4 | // Establish a global set of functions and objects for testing flow control. 5 | let failed = false 6 | function fail(errStr: string, ...inputs: any) { 7 | if (!t.failed) { 8 | console.error(t.testName, "has failed") 9 | } 10 | failed = true 11 | t.failed = true 12 | console.log("\t", errStr, ...inputs) 13 | } 14 | function log(...inputs: any) { 15 | console.log("\t", ...inputs) 16 | } 17 | let t = { 18 | failed: false, 19 | testName: "", 20 | fail, 21 | log, 22 | } 23 | function runBench(test: any) { 24 | t.failed = false 25 | t.testName = test.name 26 | console.log(t.testName, "is running") 27 | test(t) 28 | } 29 | 30 | // BenchEncryptDecryptSpeed measures the time it takes to encrypt and then 31 | // decrypt a 20 MB file. 32 | function BenchEncryptDecryptSpeed(t: any) { 33 | // Get a seed. 34 | let [seedPhrase, errGSPD] = generateSeedPhraseDeterministic("test-for-speed") 35 | if (errGSPD !== null) { 36 | t.fail(errGSPD) 37 | return 38 | } 39 | let [seed, errSPTS] = seedPhraseToSeed(seedPhrase) 40 | if (errSPTS !== null) { 41 | t.fail(errSPTS) 42 | return 43 | } 44 | 45 | // Establish the other inputs. 46 | let inode = "testFileSpeed" 47 | let revision = BigInt(0) 48 | let metadata = { 49 | filename: "testSpeed.txt", 50 | } 51 | let fileData = new Uint8Array(20 * 1000 * 1000) 52 | 53 | // Attempt to encrypt the file. 54 | let encStart = performance.now() 55 | let [encryptedData, errEF] = encryptFileSmall(seed, inode, revision, metadata, fileData) 56 | if (errEF !== null) { 57 | t.fail(errEF) 58 | return 59 | } 60 | let encStop = performance.now() 61 | t.log("time to encrypt 20 MB:", encStop - encStart) 62 | 63 | // Attempt to decrypt the file. 64 | let decStart = performance.now() 65 | let [recoveredMetadata, recoveredFileData, errDF] = decryptFileSmall(seed, inode, encryptedData) 66 | if (errDF !== null) { 67 | t.fail("received error when decrypting file", errDF) 68 | return 69 | } 70 | let decStop = performance.now() 71 | t.log("time to decrypt 20 MB:", decStop - decStart) 72 | } 73 | 74 | runBench(BenchEncryptDecryptSpeed) 75 | 76 | if (failed) { 77 | console.log() 78 | console.log("benchmarks had errors") 79 | process.exit(1) 80 | } 81 | -------------------------------------------------------------------------------- /libs/libskynet/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("dist", { recursive: true, force: true }); 3 | fs.rmSync("dist-test", { recursive: true, force: true }); 4 | fs.rmSync("node_modules", { recursive: true, force: true }); 5 | -------------------------------------------------------------------------------- /libs/libskynet/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | export default { 3 | preset: "ts-jest", 4 | resolver: "jest-ts-webcompat-resolver", 5 | testEnvironment: "node", 6 | testPathIgnorePatterns: ["dist/"], 7 | }; 8 | -------------------------------------------------------------------------------- /libs/libskynet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libskynet", 3 | "version": "0.1.9", 4 | "author": "Skynet Labs", 5 | "description": "helper library to interact with skynet's low level primitives", 6 | "main": "dist/index.js", 7 | "type": "module", 8 | "types": "dist/index.d.js", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "files": [ 13 | "/dist" 14 | ], 15 | "scripts": { 16 | "lint": "prettier -l 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json'", 17 | "clean": "node ./clean.js", 18 | "deps": "npm audit fix", 19 | "update-deps": "npm run clean && npm run deps && npm install", 20 | "update-lint": "prettier -w 'src' '*.js' '*.json' && eslint 'src' '*.js' '*.json' --fix", 21 | "update": "npm run update-deps && npm run update-lint && tsc", 22 | "spot-test": "node --no-warnings --loader ts-node/esm spotTest.ts", 23 | "benchmark": "node --no-warnings --loader ts-node/esm benchmarks.ts", 24 | "test": "jest && npm run spot-test && npm run benchmark", 25 | "build": "npm run clean && npm install && npm run lint && tsc", 26 | "prepublishOnly": "npm run clean && npm install && npm run lint && npm run test && npm run build" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "^28.1.5", 30 | "@types/node": "^17.0.23", 31 | "@typescript-eslint/eslint-plugin": "^5.19.0", 32 | "@typescript-eslint/typescript-estree": "^5.35.1", 33 | "eslint": "^8.13.0", 34 | "jest": "^28.1.2", 35 | "jest-ts-webcompat-resolver": "^1.0.0", 36 | "prettier": "^2.6.2", 37 | "skynet-js": "^4.3.0", 38 | "ts-jest": "^28.0.5", 39 | "ts-node": "^10.9.1", 40 | "tweetnacl": "^1.0.3", 41 | "typescript": "^4.7.4" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /libs/libskynet/src/apidefaultportals.ts: -------------------------------------------------------------------------------- 1 | const defaultPortalList = ["https://skynet.moe", "https://siasky.net", "https://web3portal.com"]; 2 | 3 | export { defaultPortalList }; 4 | -------------------------------------------------------------------------------- /libs/libskynet/src/apidownloadskylink.ts: -------------------------------------------------------------------------------- 1 | import { verifyDownloadResponse } from "./apidownloadverifyresponse.js"; 2 | import { progressiveFetch, progressiveFetchResult } from "./apiprogressivefetch.js"; 3 | import { defaultPortalList } from "./apidefaultportals.js"; 4 | import { addContextToErr } from "./err.js"; 5 | import { b64ToBuf } from "./encoding.js"; 6 | import { objAsString } from "./objAsString.js"; 7 | import { validateSkylink } from "./skylinkValidate.js"; 8 | import { Err } from "./types.js"; 9 | 10 | // downloadSkylink will download the provided skylink. 11 | function downloadSkylink(skylink: string): Promise<[data: Uint8Array, err: Err]> { 12 | return new Promise((resolve) => { 13 | // Get the Uint8Array of the input skylink. 14 | const [u8Link, errBTB] = b64ToBuf(skylink); 15 | if (errBTB !== null) { 16 | resolve([new Uint8Array(0), addContextToErr(errBTB, "unable to decode skylink")]); 17 | return; 18 | } 19 | const errVS = validateSkylink(u8Link); 20 | if (errVS !== null) { 21 | resolve([new Uint8Array(0), addContextToErr(errVS, "skylink is invalid")]); 22 | return; 23 | } 24 | 25 | // Prepare the download call. 26 | const endpoint = "/skynet/trustless/basesector/" + skylink; 27 | const fileDataPtr = { fileData: new Uint8Array(0), err: null }; 28 | const verifyFunction = function (response: Response): Promise { 29 | return verifyDownloadResponse(response, u8Link, fileDataPtr); 30 | }; 31 | 32 | // Perform the download call. 33 | progressiveFetch(endpoint, null, defaultPortalList, verifyFunction).then((result: progressiveFetchResult) => { 34 | // Return an error if the call failed. 35 | if (result.success !== true) { 36 | // Check for a 404. 37 | for (let i = 0; i < result.responsesFailed.length; i++) { 38 | if (result.responsesFailed[i] !== null && result.responsesFailed[i].status === 404) { 39 | resolve([new Uint8Array(0), "404"]); 40 | return; 41 | } 42 | } 43 | 44 | // Error is not a 404, return the logs as the error. 45 | const err = objAsString(result.logs); 46 | resolve([new Uint8Array(0), addContextToErr(err, "unable to complete download")]); 47 | return; 48 | } 49 | // Check if the portal is honest but the download is corrupt. 50 | if (fileDataPtr.err !== null) { 51 | resolve([new Uint8Array(0), addContextToErr(fileDataPtr.err, "download is corrupt")]); 52 | return; 53 | } 54 | resolve([fileDataPtr.fileData, null]); 55 | }); 56 | }); 57 | } 58 | 59 | export { downloadSkylink }; 60 | -------------------------------------------------------------------------------- /libs/libskynet/src/apiregistryverify.ts: -------------------------------------------------------------------------------- 1 | import { hexToBuf } from "./encoding.js"; 2 | import { addContextToErr } from "./err.js"; 3 | import { objAsString } from "./objAsString.js"; 4 | import { parseJSON } from "./parse.js"; 5 | import { verifyRegistrySignature } from "./registry.js"; 6 | import { Err } from "./types.js"; 7 | 8 | // verifyDecodedResp will verify the decoded response from a portal for a 9 | // regRead call. 10 | function verifyDecodedResp(resp: Response, data: any, pubkey: Uint8Array, datakey: Uint8Array): Err { 11 | // Status is expected to be 200. 12 | if (resp.status !== 200) { 13 | return "expected 200 response status, got: " + objAsString(resp.status); 14 | } 15 | 16 | // Verify that all required fields were provided. 17 | if (!("data" in data)) { 18 | return "expected data field in response"; 19 | } 20 | if (typeof data.data !== "string") { 21 | return "expected data field to be a string"; 22 | } 23 | if (!("revision" in data)) { 24 | return "expected revision in response"; 25 | } 26 | if (typeof data.revision !== "bigint") { 27 | return "expected revision to be a number"; 28 | } 29 | if (!("signature" in data)) { 30 | return "expected signature in response"; 31 | } 32 | if (typeof data.signature !== "string") { 33 | return "expected signature to be a string"; 34 | } 35 | 36 | // Parse out the fields we need. 37 | const [entryData, errHTB] = hexToBuf(data.data); 38 | if (errHTB !== null) { 39 | return "could not decode registry data from response"; 40 | } 41 | const [sig, errHTB2] = hexToBuf(data.signature); 42 | if (errHTB2 !== null) { 43 | return "could not decode signature from response"; 44 | } 45 | 46 | // Verify the signature. 47 | if (!verifyRegistrySignature(pubkey, datakey, entryData, data.revision, sig)) { 48 | return "signature mismatch"; 49 | } 50 | 51 | // TODO: Need to be handling type 2 registry entries here otherwise we will 52 | // be flagging non malicious portals as malicious. 53 | 54 | return null; 55 | } 56 | 57 | // verifyRegistryReadResponse will verify that the registry read response from 58 | // the portal was correct. 59 | function verifyRegistryReadResponse(resp: Response, pubkey: Uint8Array, datakey: Uint8Array): Promise { 60 | return new Promise((resolve) => { 61 | resp 62 | .text() 63 | .then((str: string) => { 64 | const [obj, errPJ] = parseJSON(str); 65 | if (errPJ !== null) { 66 | resolve(addContextToErr(errPJ, "unable to parse registry response")); 67 | return; 68 | } 69 | const errVDR = verifyDecodedResp(resp, obj, pubkey, datakey); 70 | if (errVDR !== null) { 71 | resolve(addContextToErr(errVDR, "regRead response failed verification")); 72 | return; 73 | } 74 | resolve(null); 75 | }) 76 | .catch((err: any) => { 77 | resolve(addContextToErr(objAsString(err), "unable to decode response")); 78 | }); 79 | }); 80 | } 81 | 82 | // verifyRegistryWriteResponse will verify that the response from a 83 | // registryWrite call is valid. There's not much to verify beyond looking for 84 | // the right response code, as the portal is not providing us with data, just 85 | // confirming that a write succeeded. 86 | function verifyRegistryWriteResponse(resp: Response): Promise { 87 | return new Promise((resolve) => { 88 | if (resp.status === 204) { 89 | resolve(null); 90 | } 91 | resolve("expecting 200 status code for registry write, got:" + resp.status.toString()); 92 | }); 93 | } 94 | 95 | export { verifyRegistryReadResponse, verifyRegistryWriteResponse }; 96 | -------------------------------------------------------------------------------- /libs/libskynet/src/checkObjProps.ts: -------------------------------------------------------------------------------- 1 | import { Err } from "./types.js"; 2 | 3 | // checkObjProps take an untrusted object and a list of typechecks to perform 4 | // and will check that the object adheres to the typechecks. If a type is 5 | // missing or has the wrong type, an error will be returned. This is intended 6 | // to be used to check untrusted objects after they get decoded from JSON. This 7 | // is particularly useful when receiving objects from untrusted entities over 8 | // the network or over postMessage. 9 | // 10 | // Below is an example object, followed by the call that you would make to 11 | // checkObj to verify the object. 12 | // 13 | // const expectedObj = { 14 | // aNum: 35, 15 | // aStr: "hi", 16 | // aBig: 10n, 17 | // }; 18 | // 19 | // const err = checkObjProps(expectedObj, [ 20 | // ["aNum", "number"], 21 | // ["aStr", "string"], 22 | // ["aBig", "bigint"], 23 | // ]); 24 | // 25 | // Over time, we intend to extend this function to support more types than just 26 | // the default types supported by javascript. For example, we intend to add 27 | // special cases for arrays, and for cryptographic objects. 28 | function checkObjProps(obj: any, checks: [string, string][]): Err { 29 | for (let i = 0; i < checks.length; i++) { 30 | const check = checks[i]; 31 | const type = typeof obj[check[0]]; 32 | if (type !== check[1]) { 33 | return "check failed, expecting " + check[1] + " got " + type; 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | export { checkObjProps }; 40 | -------------------------------------------------------------------------------- /libs/libskynet/src/ed25519.test.ts: -------------------------------------------------------------------------------- 1 | import { ed25519KeypairFromEntropy, ed25519Sign, ed25519Verify } from "./ed25519.js"; 2 | import { sha512 } from "./sha512.js"; 3 | 4 | test("ed25519", () => { 5 | // Make a keypair with some entropy, then test that signing and verifying 6 | // don't fail. 7 | const entropy = sha512(new TextEncoder().encode("fake entropy")); 8 | const [keypair, errKPFE] = ed25519KeypairFromEntropy(entropy.slice(0, 32)); 9 | expect(errKPFE).toBe(null); 10 | const message = new TextEncoder().encode("fake message"); 11 | const [signature, errS] = ed25519Sign(message, keypair.secretKey); 12 | expect(errS).toBe(null); 13 | const validSig = ed25519Verify(message, signature, keypair.publicKey); 14 | expect(validSig).toBe(true); 15 | }); 16 | -------------------------------------------------------------------------------- /libs/libskynet/src/encoding.test.ts: -------------------------------------------------------------------------------- 1 | import { bufToHex, bufToB64, decodeU64, encodeU64, hexToBuf } from "../src/encoding"; 2 | 3 | test("encodeAndDecodeU64", () => { 4 | const tests = [0n, 1n, 2n, 35n, 500n, 12345n, 642156n, 9591335n, 64285292n]; 5 | for (let i = 0; i < tests.length; i++) { 6 | const [enc, errEU64] = encodeU64(tests[i]); 7 | expect(errEU64).toBe(null); 8 | const [dec, errDU64] = decodeU64(enc); 9 | expect(errDU64).toBe(null); 10 | expect(dec).toBe(tests[i]); 11 | } 12 | }); 13 | 14 | test("bufToB64", () => { 15 | const tests = [ 16 | { trial: new Uint8Array(0), result: "" }, 17 | { trial: new Uint8Array([1]), result: "AQ" }, 18 | { trial: new Uint8Array([1, 2]), result: "AQI" }, 19 | { trial: new Uint8Array([255]), result: "_w" }, 20 | { trial: new Uint8Array([23, 51, 0]), result: "FzMA" }, 21 | { trial: new Uint8Array([0]), result: "AA" }, 22 | { trial: new Uint8Array([0, 0, 0]), result: "AAAA" }, 23 | { trial: new Uint8Array([30, 1, 3, 45, 129, 127]), result: "HgEDLYF_" }, 24 | { trial: new Uint8Array([155, 196, 150, 83, 71, 54, 205, 231, 249, 34]), result: "m8SWU0c2zef5Ig" }, 25 | { trial: new Uint8Array([57, 58, 59, 60, 61, 62, 63, 64]), result: "OTo7PD0-P0A" }, 26 | ]; 27 | for (let i = 0; i < tests.length; i++) { 28 | const result = bufToB64(tests[i].trial); 29 | expect(result.length).toBe(tests[i].result.length); 30 | for (let j = 0; j < result.length; j++) { 31 | expect(result[j]).toBe(tests[i].result[j]); 32 | } 33 | } 34 | }); 35 | 36 | test("bufToHexAndBufToHex", () => { 37 | const tests = [ 38 | { trial: new Uint8Array(0), result: "" }, 39 | { trial: new Uint8Array([1]), result: "01" }, 40 | { trial: new Uint8Array([1, 2]), result: "0102" }, 41 | { trial: new Uint8Array([255]), result: "ff" }, 42 | { trial: new Uint8Array([23, 51, 0]), result: "173300" }, 43 | { trial: new Uint8Array([3, 7, 63, 127, 200, 5]), result: "03073f7fc805" }, 44 | { trial: new Uint8Array([0]), result: "00" }, 45 | { trial: new Uint8Array([0, 0, 0]), result: "000000" }, 46 | ]; 47 | 48 | // Test hexToBuf 49 | for (let i = 0; i < tests.length; i++) { 50 | const result = bufToHex(tests[i].trial); 51 | expect(result.length).toBe(tests[i].result.length); 52 | for (let j = 0; j < result.length; j++) { 53 | expect(result[j]).toBe(tests[i].result[j]); 54 | } 55 | } 56 | 57 | // Test bufToHex. 58 | for (let i = 0; i < tests.length; i++) { 59 | const [result, err] = hexToBuf(tests[i].result); 60 | expect(err).toBe(null); 61 | expect(result.length).toBe(tests[i].trial.length); 62 | for (let j = 0; j < result.length; j++) { 63 | expect(result[j]).toBe(tests[i].trial[j]); 64 | } 65 | 66 | // Check that upper case is also okay. 67 | const [result2, err2] = hexToBuf(tests[i].result.toUpperCase()); 68 | expect(err2).toBe(null); 69 | expect(result2.length).toBe(tests[i].trial.length); 70 | for (let j = 0; j < result2.length; j++) { 71 | expect(result2[j]).toBe(tests[i].trial[j]); 72 | } 73 | } 74 | 75 | // Create tests to check for invalid inputs. 76 | const invalids = ["0", "000", "aX", "123O", "XX"]; 77 | for (let i = 0; i < invalids.length; i++) { 78 | const [, err] = hexToBuf(invalids[i]); 79 | if (err === null) { 80 | console.log(invalids[i]); 81 | } 82 | expect(err).not.toBe(null); 83 | } 84 | }); 85 | -------------------------------------------------------------------------------- /libs/libskynet/src/encrypt.ts: -------------------------------------------------------------------------------- 1 | import { encodeU64 } from "./encoding.js"; 2 | import { SHA512_HASH_SIZE, sha512 } from "./sha512.js"; 3 | 4 | // otpEncrypt takes a key and some data and encrypts the data with the key. The 5 | // encryption happens by generating a sequence of bytes using sha512 hashes and 6 | // then xor'ing those bytes with the data. This gives otpEncrypt similar 7 | // security properties to a one-time-pad - which means that the same key cannot 8 | // be used twice! 9 | // 10 | // It also means that there is no authentication on the data, and that an 11 | // attacker could flip bits undetected if an authentication layer is not added 12 | // on top. 13 | // 14 | // Data is encrypted in-place. The optional value 'skip' allows the caller to 15 | // specify a number of bytes to skip initially. 16 | // 17 | // NOTE: otpEncrypt can be useful over other encryption methods because it does 18 | // not introduce any new dependencies. For the Skynet Kernel bootloader, the 19 | // only cryptography present is ed25519 signatures (which includes sha512 as a 20 | // dependency). This is a tiny piece of code that can provide encryption 21 | // support without needing to add a full encryption library as a dependency. 22 | // 23 | // WARNING: otpEncrypt is not a "safe" function. It's a useful cryptographic 24 | // primitive, but there are easy ways to misuse it and create insecure 25 | // encryption schemes. Please avoid using this function unless you have a 26 | // strong understanding of encryption techniques and typical encryption 27 | // attacks. 28 | function otpEncrypt(key: Uint8Array, data: Uint8Array, skip = 0): Uint8Array { 29 | // Build an array to hold the preimage for each step of encryption. We are 30 | // just going to be altering the final 8 bytes as we encrypt the file. 31 | const preimageHolder = new Uint8Array(key.length + 8); 32 | preimageHolder.set(key, 0); 33 | 34 | // Iterate over the data and encrypt each section. 35 | for (let i = skip; i < data.length; i += SHA512_HASH_SIZE) { 36 | // Set the nonce for this shard and then create the pad data. The error of 37 | // encodeU64 is ignored because it'll only error if the passed in data is 38 | // larger than 2^64 bytes, which is not likely. It was decided that the 39 | // tradeoff of not having to check an error every time after calling 40 | // otpEncrypt was worth ignoring the error here - this is an unusual 41 | // omission and is generally discouraged. 42 | const [iBytes] = encodeU64(BigInt(i)); 43 | preimageHolder.set(iBytes, key.length); 44 | const keyData = sha512(preimageHolder); 45 | 46 | // XOR the keyData with the data. Watch for out-of-bounds on the 47 | // file data. 48 | for (let j = 0; j < keyData.length; j++) { 49 | if (data.length <= i + j) { 50 | break; 51 | } 52 | data[i + j] = data[i + j] ^ keyData[j]; 53 | } 54 | } 55 | return data; 56 | } 57 | 58 | export { otpEncrypt }; 59 | -------------------------------------------------------------------------------- /libs/libskynet/src/err.ts: -------------------------------------------------------------------------------- 1 | import { objAsString } from "./objAsString.js"; 2 | 3 | // addContextToErr is a helper function that standardizes the formatting of 4 | // adding context to an error. 5 | // 6 | // NOTE: To protect against accidental situations where an Error type or some 7 | // other type is provided instead of a string, we wrap both of the inputs with 8 | // objAsString before returning them. This prevents runtime failures. 9 | function addContextToErr(err: any, context: string): string { 10 | if (err === null || err === undefined) { 11 | err = "[no error provided]"; 12 | } 13 | return objAsString(context) + ": " + objAsString(err); 14 | } 15 | 16 | export { addContextToErr }; 17 | -------------------------------------------------------------------------------- /libs/libskynet/src/errTracker.ts: -------------------------------------------------------------------------------- 1 | // errTracker.ts defines an 'ErrTracker' type which keeps track of historical 2 | // errors. When the number of errors gets too large, it randomly starts pruning 3 | // errors. It always keeps 250 of the most recent errors, and then keeps up to 4 | // 500 historic errors, where the first few errors after runtime are always 5 | // kept, and the ones in the middle are increasingly likely to be omitted from 6 | // the history. 7 | 8 | import { Err } from "./types.js"; 9 | 10 | // MAX_ERRORS defines the maximum number of errors that will be held in the 11 | // HistoricErr object. 12 | const MAX_ERRORS = 1000; 13 | 14 | // HistoricErr is a wrapper that adds a date to the Err type. 15 | interface HistoricErr { 16 | err: Err; 17 | date: Date; 18 | } 19 | 20 | // ErrTracker keeps track of errors that have happened, randomly dropping 21 | // errors to prevent the tracker from using too much memory if there happen to 22 | // be a large number of errors. 23 | interface ErrTracker { 24 | recentErrs: HistoricErr[]; 25 | oldErrs: HistoricErr[]; 26 | 27 | addErr: (err: Err) => void; 28 | viewErrs: () => HistoricErr[]; 29 | } 30 | 31 | // newErrTracker returns an ErrTracker object that is ready to have errors 32 | // added to it. 33 | function newErrTracker(): ErrTracker { 34 | const et: ErrTracker = { 35 | recentErrs: [], 36 | oldErrs: [], 37 | 38 | addErr: function (err: Err): void { 39 | addHistoricErr(et, err); 40 | }, 41 | viewErrs: function (): HistoricErr[] { 42 | return viewErrs(et); 43 | }, 44 | }; 45 | return et; 46 | } 47 | 48 | // addHistoricErr is a function that will add an error to a set of historic 49 | // errors. It uses randomness to prune errors once the error object is too 50 | // large. 51 | function addHistoricErr(et: ErrTracker, err: Err): void { 52 | // Add this error to the set of most recent errors. 53 | et.recentErrs.push({ 54 | err, 55 | date: new Date(), 56 | }); 57 | 58 | // Determine whether some of the most recent errors need to be moved into 59 | // logTermErrs. If the length of the mostRecentErrs is not at least half of 60 | // the MAX_ERRORS, we don't need to do anything. 61 | if (et.recentErrs.length < MAX_ERRORS / 2) { 62 | return; 63 | } 64 | 65 | // Iterate through the recentErrs. For the first half of the recentErrs, we 66 | // will use randomness to either toss them or move them to oldErrs. The 67 | // second half of the recentErrs will be kept as the new recentErrs array. 68 | const newRecentErrs = []; 69 | for (let i = 0; i < et.recentErrs.length; i++) { 70 | // If we are in the second half of the array, add the element to 71 | // newRecentErrs. 72 | if (i > et.recentErrs.length / 2) { 73 | newRecentErrs.push(et.recentErrs[i]); 74 | continue; 75 | } 76 | 77 | // We are in the first half of the array, use a random number to add the 78 | // error oldErrs probabilistically. 79 | const rand = Math.random(); 80 | const target = et.oldErrs.length / (MAX_ERRORS / 2); 81 | if (rand > target || et.oldErrs.length < 25) { 82 | et.oldErrs.push(et.recentErrs[i]); 83 | } 84 | } 85 | et.recentErrs = newRecentErrs; 86 | } 87 | 88 | // viewErrs returns the list of errors that have been retained by the 89 | // HistoricErr object. 90 | function viewErrs(et: ErrTracker): HistoricErr[] { 91 | const finalErrs: HistoricErr[] = []; 92 | for (let i = 0; i < et.oldErrs.length; i++) { 93 | finalErrs.push(et.oldErrs[i]); 94 | } 95 | for (let i = 0; i < et.recentErrs.length; i++) { 96 | finalErrs.push(et.recentErrs[i]); 97 | } 98 | return finalErrs; 99 | } 100 | 101 | export { ErrTracker, HistoricErr, newErrTracker }; 102 | -------------------------------------------------------------------------------- /libs/libskynet/src/index.ts: -------------------------------------------------------------------------------- 1 | export { downloadSkylink } from "./apidownloadskylink.js"; 2 | export { verifyDownload } from "./apidownloadverify.js"; 3 | export { fileDataObj, verifyDownloadResponse } from "./apidownloadverifyresponse.js"; 4 | export { progressiveFetch, progressiveFetchResult } from "./apiprogressivefetch.js"; 5 | export { verifyRegistryReadResponse, verifyRegistryWriteResponse } from "./apiregistryverify.js"; 6 | export { defaultPortalList } from "./apidefaultportals.js"; 7 | export { BLAKE2B_HASH_SIZE, blake2b } from "./blake2b.js"; 8 | export { checkObjProps } from "./checkObjProps.js"; 9 | export { DICTIONARY_UNIQUE_PREFIX, dictionary } from "./dictionary.js"; 10 | export { Ed25519Keypair, ed25519KeypairFromEntropy, ed25519Sign, ed25519Verify } from "./ed25519.js"; 11 | export { 12 | b64ToBuf, 13 | bufToB64, 14 | bufToHex, 15 | bufToStr, 16 | decodeU64, 17 | encodePrefixedBytes, 18 | encodeU64, 19 | hexToBuf, 20 | } from "./encoding.js"; 21 | export { otpEncrypt } from "./encrypt.js"; 22 | export { addContextToErr } from "./err.js"; 23 | export { ErrTracker, HistoricErr, newErrTracker } from "./errTracker.js"; 24 | export { decryptFileSmall, encryptFileSmall } from "./filePrivate.js"; 25 | export { namespaceInode } from "./inode.js"; 26 | export { blake2bAddLeafBytesToProofStack, blake2bMerkleRoot, blake2bProofStackRoot } from "./merkle.js"; 27 | export { objAsString } from "./objAsString.js"; 28 | export { parseJSON } from "./parse.js"; 29 | export { 30 | computeRegistrySignature, 31 | deriveRegistryEntryID, 32 | entryIDToSkylink, 33 | skylinkToResolverEntryData, 34 | taggedRegistryEntryKeys, 35 | verifyRegistrySignature, 36 | } from "./registry.js"; 37 | export { 38 | SEED_BYTES, 39 | deriveChildSeed, 40 | deriveMyskyRootKeypair, 41 | generateSeedPhraseDeterministic, 42 | seedToChecksumWords, 43 | seedPhraseToSeed, 44 | validSeedPhrase, 45 | } from "./seed.js"; 46 | export { SHA512_HASH_SIZE, sha512 } from "./sha512.js"; 47 | export { SKYLINK_U8_V1_V2_LENGTH, parseSkylinkBitfield, skylinkV1Bitfield } from "./skylinkBitfield.js"; 48 | export { validateSkyfileMetadata, validateSkyfilePath, validateSkylink } from "./skylinkValidate.js"; 49 | export { verifyResolverLinkProofs } from "./skylinkVerifyResolver.js"; 50 | export { jsonStringify } from "./stringifyJSON.js"; 51 | export { DataFn, Err, ErrFn, ErrTuple, KernelAuthStatus, RequestOverrideResponse, SkynetPortal } from "./types.js"; 52 | export { validateObjPropTypes } from "./validateObjPropTypes.js"; 53 | -------------------------------------------------------------------------------- /libs/libskynet/src/inode.ts: -------------------------------------------------------------------------------- 1 | // inode.ts is a file with helper functions for manging inodes. An inode is the 2 | // canonical name for a file within a filesystem. Inodes on Skynet are 3 | // specifiers that allow you to look up a specfic file on Skynet. 4 | 5 | import { bufToB64 } from "./encoding.js"; 6 | import { sha512 } from "./sha512.js"; 7 | import { Err } from "./types.js"; 8 | 9 | // namespaceInode is a function for namespacing inodes based on the type of 10 | // file that is being used. Two files of different types that use this function 11 | // will have fully non-overlapping namespaces for inodes, meaning that two 12 | // files of different types but that have the same name will point to different 13 | // locations on Skynet. 14 | function namespaceInode(filetype: string, inode: string): [string, Err] { 15 | // We pad out the filetype to 32 characters to ensure that two different 16 | // filetypes can never have a filetype+inode combination that will collide. 17 | // If the filetype is different, the final result will definitely also be 18 | // different. 19 | if (filetype.length > 32) { 20 | return ["", "filetype can be at most 32 characters"]; 21 | } 22 | const paddedFiletype = filetype.padEnd(32, "_"); 23 | const hashPreimage = new TextEncoder().encode(paddedFiletype + inode); 24 | 25 | // We hash the result to make it smaller. Because we use this as an 26 | // encryption key and not for authentication, our security model only 27 | // requires 16 bits of entropy. We therefore only take the first 16 bytes 28 | // of the hash and return the base64 encoded string. 29 | const fullHash = sha512(hashPreimage); 30 | const quarterHash = fullHash.slice(0, 16); 31 | const b64 = bufToB64(quarterHash); 32 | return [b64, null]; 33 | } 34 | 35 | export { namespaceInode }; 36 | -------------------------------------------------------------------------------- /libs/libskynet/src/objAsString.test.ts: -------------------------------------------------------------------------------- 1 | import { objAsString } from "../src/objAsString"; 2 | 3 | test("objAsString", () => { 4 | // Try undefined. 5 | let undefinedVar; 6 | const undefinedResult = objAsString(undefinedVar); 7 | expect(undefinedResult).toBe("[cannot convert undefined to string]"); 8 | 9 | // Try null. 10 | const nullVar = null; 11 | const nullResult = objAsString(nullVar); 12 | expect(nullResult).toBe("[cannot convert null to string]"); 13 | 14 | // Try a string. 15 | const strResult = objAsString("asdf"); 16 | expect(strResult).toBe("asdf"); 17 | const strVar = "asdfasdf"; 18 | const strResult2 = objAsString(strVar); 19 | expect(strResult2).toBe("asdfasdf"); 20 | 21 | // Try an object. 22 | const objVar = { a: "b", b: 7 }; 23 | const objResult = objAsString(objVar); 24 | expect(objResult).toBe('{"a":"b","b":7}'); 25 | 26 | // Try an object with a defined toString that is a function. 27 | objVar.toString = function () { 28 | return "b7"; 29 | }; 30 | const objResult3 = objAsString(objVar); 31 | expect(objResult3).toBe("b7"); 32 | 33 | // Try an object with a defined toString that is not a function. We need to 34 | // specifiy 'as any' because we already made 'toString' a string, and now we 35 | // are redefining the field with a new type. 36 | (objVar as any).toString = "b7"; 37 | const objResult2 = objAsString(objVar); 38 | expect(objResult2).toBe('{"a":"b","b":7,"toString":"b7"}'); 39 | 40 | // Try testing an error object. 41 | const err1 = new Error("this is an error"); 42 | const err1Result = objAsString(err1); 43 | expect(err1Result).toBe("this is an error"); 44 | }); 45 | -------------------------------------------------------------------------------- /libs/libskynet/src/objAsString.ts: -------------------------------------------------------------------------------- 1 | // objAsString will try to return the provided object as a string. If the 2 | // object is already a string, it will be returned without modification. If the 3 | // object is an 'Error', the message of the error will be returned. If the object 4 | // has a toString method, the toString method will be called and the result 5 | // will be returned. If the object is null or undefined, a special string will 6 | // be returned indicating that the undefined/null object cannot be converted to 7 | // a string. In all other cases, JSON.stringify is used. If JSON.stringify 8 | // throws an exception, a message "[could not provide object as string]" will 9 | // be returned. 10 | // 11 | // NOTE: objAsString is intended to produce human readable output. It is lossy, 12 | // and it is not intended to be used for serialization. 13 | function objAsString(obj: any): string { 14 | // Check for undefined input. 15 | if (obj === undefined) { 16 | return "[cannot convert undefined to string]"; 17 | } 18 | if (obj === null) { 19 | return "[cannot convert null to string]"; 20 | } 21 | 22 | // Parse the error into a string. 23 | if (typeof obj === "string") { 24 | return obj; 25 | } 26 | 27 | // Check if the object is an error, and return the message of the error if 28 | // so. 29 | if (obj instanceof Error) { 30 | return obj.message; 31 | } 32 | 33 | // Check if the object has a 'toString' method defined on it. To ensure 34 | // that we don't crash or throw, check that the toString is a function, and 35 | // also that the return value of toString is a string. 36 | if (Object.prototype.hasOwnProperty.call(obj, "toString")) { 37 | if (typeof obj.toString === "function") { 38 | const str = obj.toString(); 39 | if (typeof str === "string") { 40 | return str; 41 | } 42 | } 43 | } 44 | 45 | // If the object does not have a custom toString, attempt to perform a 46 | // JSON.stringify. We use a lot of bigints in libskynet, and calling 47 | // JSON.stringify on an object with a bigint will cause a throw, so we add 48 | // some custom handling to allow bigint objects to still be encoded. 49 | try { 50 | return JSON.stringify(obj, (_, v) => { 51 | if (typeof v === "bigint") { 52 | return v.toString(); 53 | } 54 | return v; 55 | }); 56 | } catch (err: any) { 57 | if (err !== undefined && typeof err.message === "string") { 58 | return `[stringify failed]: ${err.message}`; 59 | } 60 | return "[stringify failed]"; 61 | } 62 | } 63 | 64 | export { objAsString }; 65 | -------------------------------------------------------------------------------- /libs/libskynet/src/registry.test.ts: -------------------------------------------------------------------------------- 1 | import { deriveRegistryEntryID, taggedRegistryEntryKeys } from "../src/registry.js"; 2 | import { generateSeedPhraseDeterministic, seedPhraseToSeed } from "../src/seed.js"; 3 | 4 | test("registry", () => { 5 | // The fake seed needs to be 16 bytes so that taggedRegistryEntryKeys will 6 | // accept it as a real seed. 7 | const fakeSeed = new TextEncoder().encode("1234567890123456"); 8 | const [, , errREK1] = taggedRegistryEntryKeys(fakeSeed, "", ""); 9 | expect(errREK1).toBe(null); 10 | const [seedPhrase, errGSP] = generateSeedPhraseDeterministic("TestRegistry"); 11 | expect(errGSP).toBe(null); 12 | const [seed, errVSP] = seedPhraseToSeed(seedPhrase); 13 | expect(errVSP).toBe(null); 14 | 15 | // Check that keypairs are deterministic. 16 | const [keypair2, datakey2, errREK2] = taggedRegistryEntryKeys(seed, "test-keypair", "test-datakey"); 17 | expect(errREK2).toBe(null); 18 | const [keypair3, datakey3, errREK3] = taggedRegistryEntryKeys(seed, "test-keypair", "test-datakey"); 19 | expect(errREK3).toBe(null); 20 | expect(datakey2).toEqual(datakey3); 21 | expect(keypair2).toEqual(keypair3); 22 | 23 | // Check that changing the keypair also changes the datakey even if the 24 | // datakeyTag is unchanged. 25 | const [keypair4, datakey4, errREK4] = taggedRegistryEntryKeys(seed, "test-keypair2", "test-datakey"); 26 | expect(errREK4).toBe(null); 27 | expect(keypair3).not.toEqual(keypair4); 28 | expect(datakey3).not.toEqual(datakey4); 29 | 30 | // Check that changing the datakey doesn't change the keypair. 31 | const [keypair5, datakey5, errREK5] = taggedRegistryEntryKeys(seed, "test-keypair2", "test-datakey2"); 32 | expect(errREK5).toBe(null); 33 | expect(keypair4).toEqual(keypair5); 34 | expect(datakey4).not.toEqual(datakey5); 35 | 36 | // Check that we can derive a registry entry id. 37 | const [, errDREID] = deriveRegistryEntryID(keypair5.publicKey, datakey5); 38 | expect(errDREID).toBe(null); 39 | }); 40 | -------------------------------------------------------------------------------- /libs/libskynet/src/seed.test.ts: -------------------------------------------------------------------------------- 1 | import { dictionary } from "../src/dictionary.js"; 2 | import { 3 | deriveMyskyRootKeypair, 4 | generateSeedPhraseDeterministic, 5 | seedPhraseToSeed, 6 | validSeedPhrase, 7 | } from "../src/seed.js"; 8 | 9 | test("generateSeedPhraseDeterministic", () => { 10 | // Generate three seed phrases, two matching and one not matching. Make sure 11 | // they match and don't match as expected. 12 | const [phraseTestInput, err3] = generateSeedPhraseDeterministic("Test"); 13 | const [phraseTestInput2, err4] = generateSeedPhraseDeterministic("Test"); 14 | const [phraseTestInput3, err5] = generateSeedPhraseDeterministic("Test2"); 15 | expect(err3).toBe(null); 16 | expect(err4).toBe(null); 17 | expect(err5).toBe(null); 18 | expect(phraseTestInput).toBe(phraseTestInput2); 19 | expect(phraseTestInput).not.toBe(phraseTestInput3); 20 | 21 | // Check that both seed phrases are valid. 22 | const errVSP1 = validSeedPhrase(phraseTestInput); 23 | const errVSP2 = validSeedPhrase(phraseTestInput3); 24 | expect(errVSP1).toBe(null); 25 | expect(errVSP2).toBe(null); 26 | 27 | // Check that the generated seeds follow the 13th word rule, which is that 28 | // the 13th word must always be from the first 256 entries in the dictionary 29 | // (this keeps the final 2 bits clear) 30 | for (let i = 0; i < 128; i++) { 31 | const [phrase, err] = generateSeedPhraseDeterministic(i.toString()); 32 | expect(err).toBe(null); 33 | 34 | let found = false; 35 | const words = phrase.split(" "); 36 | for (let j = 0; j < 256; j++) { 37 | if (words[12] === dictionary[j]) { 38 | found = true; 39 | break; 40 | } 41 | } 42 | expect(found).toBe(true); 43 | } 44 | }); 45 | 46 | // TestMyskyEquivalence is a test that checks that the way libskynet derives 47 | // the mysky seed for a user matches the code that derived a mysky seed for a 48 | // user in skynet-mysky. Following the test are some unique dependencies so 49 | // that the simulated mysky derivation is as close to the original code as 50 | // possible. 51 | import nacl from "tweetnacl"; 52 | const SALT_ROOT_DISCOVERABLE_KEY = "root discoverable key"; 53 | function genKeyPairFromSeed(seed: Uint8Array) { 54 | const hash = hashWithSalt(seed, SALT_ROOT_DISCOVERABLE_KEY); 55 | return genKeyPairFromHash(hash); 56 | } 57 | function hashWithSalt(message: Uint8Array, salt: string): Uint8Array { 58 | return s512(new Uint8Array([...s512(salt), ...s512(message)])); 59 | } 60 | function s512(message: Uint8Array | string): Uint8Array { 61 | if (typeof message === "string") { 62 | return nacl.hash(stringToUint8ArrayUtf8(message)); 63 | } 64 | return nacl.hash(message); 65 | } 66 | function stringToUint8ArrayUtf8(str: string) { 67 | return Uint8Array.from(Buffer.from(str, "utf-8")); 68 | } 69 | function genKeyPairFromHash(hash: Uint8Array) { 70 | const hashBytes = hash.slice(0, 32); 71 | const { publicKey, secretKey } = nacl.sign.keyPair.fromSeed(hashBytes); 72 | return [publicKey, secretKey]; 73 | } 74 | test("myskyEquivalence", () => { 75 | // Get a seed. 76 | const [seedPhrase, errGSPD] = generateSeedPhraseDeterministic("test-for-mysky"); 77 | expect(errGSPD).toBe(null); 78 | const [seed, errVSP] = seedPhraseToSeed(seedPhrase); 79 | expect(errVSP).toBe(null); 80 | 81 | const [pkOld, skOld] = genKeyPairFromSeed(seed); 82 | const keypair = deriveMyskyRootKeypair(seed); 83 | expect(pkOld.length).toBe(keypair.publicKey.length); 84 | for (let i = 0; i < pkOld.length; i++) { 85 | expect(pkOld[i]).toBe(keypair.publicKey[i]); 86 | } 87 | expect(skOld.length).toBe(keypair.secretKey.length); 88 | for (let i = 0; i < skOld.length; i++) { 89 | expect(skOld[i]).toBe(keypair.secretKey[i]); 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /libs/libskynet/src/skylinkBitfield.test.ts: -------------------------------------------------------------------------------- 1 | import { skylinkV1Bitfield, parseSkylinkBitfield } from "../src/skylinkBitfield.js"; 2 | 3 | test.each([ 4 | { dataSize: 0n, expectedFetchSize: 4096n }, 5 | { dataSize: 1n, expectedFetchSize: 4096n }, 6 | { dataSize: 100n, expectedFetchSize: 4096n }, 7 | { dataSize: 200n, expectedFetchSize: 4096n }, 8 | { dataSize: 4095n, expectedFetchSize: 4096n }, 9 | { dataSize: 4096n, expectedFetchSize: 4096n }, 10 | { dataSize: 4097n, expectedFetchSize: 8192n }, 11 | { dataSize: 8191n, expectedFetchSize: 8192n }, 12 | { dataSize: 8192n, expectedFetchSize: 8192n }, 13 | { dataSize: 8193n, expectedFetchSize: 12288n }, 14 | { dataSize: 12287n, expectedFetchSize: 12288n }, 15 | { dataSize: 12288n, expectedFetchSize: 12288n }, 16 | { dataSize: 12289n, expectedFetchSize: 16384n }, 17 | { dataSize: 16384n, expectedFetchSize: 16384n }, 18 | { dataSize: 32767n, expectedFetchSize: 32768n }, 19 | { dataSize: 32768n, expectedFetchSize: 32768n }, 20 | { dataSize: 32769n, expectedFetchSize: 36864n }, 21 | { dataSize: 36863n, expectedFetchSize: 36864n }, 22 | { dataSize: 36864n, expectedFetchSize: 36864n }, 23 | { dataSize: 36865n, expectedFetchSize: 40960n }, 24 | { dataSize: 45056n, expectedFetchSize: 45056n }, 25 | { dataSize: 45057n, expectedFetchSize: 49152n }, 26 | { dataSize: 65536n, expectedFetchSize: 65536n }, 27 | { dataSize: 65537n, expectedFetchSize: 73728n }, 28 | { dataSize: 106496n, expectedFetchSize: 106496n }, 29 | { dataSize: 106497n, expectedFetchSize: 114688n }, 30 | { dataSize: 163840n, expectedFetchSize: 163840n }, 31 | { dataSize: 163841n, expectedFetchSize: 180224n }, 32 | { dataSize: 491520n, expectedFetchSize: 491520n }, 33 | { dataSize: 491521n, expectedFetchSize: 524288n }, 34 | { dataSize: 720896n, expectedFetchSize: 720896n }, 35 | { dataSize: 720897n, expectedFetchSize: 786432n }, 36 | { dataSize: 1572864n, expectedFetchSize: 1572864n }, 37 | { dataSize: 1572865n, expectedFetchSize: 1703936n }, 38 | { dataSize: 3407872n, expectedFetchSize: 3407872n }, 39 | { dataSize: 3407873n, expectedFetchSize: 3670016n }, 40 | ])("skylinkV1Bitfield with data size $dataSize", ({ dataSize, expectedFetchSize }) => { 41 | const skylink = new Uint8Array(34); 42 | const [bitfield, errSVB] = skylinkV1Bitfield(dataSize); 43 | expect(errSVB).toBe(null); 44 | skylink.set(bitfield, 0); 45 | const [version, offset, fetchSize, errPSB] = parseSkylinkBitfield(skylink); 46 | expect(errPSB).toBe(null); 47 | expect(version).toBe(1n); 48 | expect(offset).toBe(0n); 49 | expect(fetchSize).toBe(expectedFetchSize); 50 | }); 51 | -------------------------------------------------------------------------------- /libs/libskynet/src/skylinkValidate.test.ts: -------------------------------------------------------------------------------- 1 | import { validateSkyfilePath } from "../src/skylinkValidate.js"; 2 | 3 | test.each([ 4 | { path: "test", result: null }, 5 | { path: "test/subtrial", result: null }, 6 | { path: "test/subtrial.ext", result: null }, 7 | { path: "test/trial.ext/subtrial.ext", result: null }, 8 | { path: ".foo", result: null }, 9 | { path: ".foo/.bar", result: null }, 10 | { path: "foo/.bar", result: null }, 11 | { path: "/", result: "metdata.Filename cannot start with /" }, 12 | { path: "", result: "path cannot be blank" }, 13 | { path: ".", result: "path cannot be ." }, 14 | { path: "./", result: "metdata.Filename cannot start with ./" }, 15 | { path: "a//b", result: "path cannot have an empty element, cannot contain //" }, 16 | { path: "a/./b", result: "path cannot have a . element" }, 17 | { path: "a/../b", result: "path cannot have a .. element" }, 18 | { path: "../a/b", result: "metdata.Filename cannot start with ../" }, 19 | { path: "/sometrial", result: "metdata.Filename cannot start with /" }, 20 | { path: "sometrial/", result: "path cannot have an empty element, cannot contain //" }, 21 | ])("testValidateSkyfilePath with path '$path'", ({ path, result }) => { 22 | expect(validateSkyfilePath(path)).toBe(result); 23 | }); 24 | -------------------------------------------------------------------------------- /libs/libskynet/src/stringifyJSON.test.ts: -------------------------------------------------------------------------------- 1 | import { jsonStringify } from "./stringifyJSON.js"; 2 | 3 | test("testJSONStringify", () => { 4 | // Check that the function works as expected with basic input. 5 | const basicObj = { 6 | test: 5, 7 | }; 8 | const [str1, err1] = jsonStringify(basicObj); 9 | expect(err1).toBe(null); 10 | 11 | // Count the number of quotes in str1, we are expecting 2. 12 | let quotes = 0; 13 | for (let i = 0; i < str1.length; i++) { 14 | if (str1[i] === '"') { 15 | quotes += 1; 16 | } 17 | } 18 | expect(quotes).toBe(2); 19 | 20 | // Try encoding a bignum. 21 | const bigNumObj = { 22 | test: 5n, 23 | testBig: 122333444455555666666777777788888888999999999000000000012345n, 24 | }; 25 | const [str2, err2] = jsonStringify(bigNumObj); 26 | expect(err2).toBe(null); 27 | // Count the number of quotes in str2, we are expecting 4. 28 | quotes = 0; 29 | for (let i = 0; i < str2.length; i++) { 30 | if (str2[i] === '"') { 31 | quotes += 1; 32 | } 33 | } 34 | expect(quotes).toBe(4); 35 | }); 36 | -------------------------------------------------------------------------------- /libs/libskynet/src/stringifyJSON.ts: -------------------------------------------------------------------------------- 1 | import { addContextToErr } from "./err.js"; 2 | import { objAsString } from "./objAsString.js"; 3 | import { Err } from "./types.js"; 4 | 5 | // jsonStringify is a replacement for JSON.stringify that returns an error 6 | // rather than throwing. 7 | function jsonStringify(obj: any): [string, Err] { 8 | try { 9 | const str = JSON.stringify(obj, (_, v) => { 10 | if (typeof v === "bigint") { 11 | return Number(v); 12 | } 13 | return v; 14 | }); 15 | return [str, null]; 16 | } catch (err) { 17 | return ["", addContextToErr(objAsString(err), "unable to stringify object")]; 18 | } 19 | } 20 | 21 | export { jsonStringify }; 22 | -------------------------------------------------------------------------------- /libs/libskynet/src/types.ts: -------------------------------------------------------------------------------- 1 | // DataFn can take any object as input and has no return value. The input is 2 | // allowed to be undefined. 3 | type DataFn = (data?: any) => void; 4 | 5 | // Err is an error type that is either a string or a null. If the value is 6 | // null, that indicates that there was no error. If the value is a string, it 7 | // indicates that there was an error and the string contains the error message. 8 | // 9 | // The skynet libraries prefer this error type to the standard Error type 10 | // because many times skynet libraries need to pass errors over postMessage, 11 | // and the 'Error' type is not able to be sent over postMessage. 12 | type Err = string | null; 13 | 14 | // ErrFn must take an error message as input. The input is not allowed to be 15 | // undefined or null, there must be an error. 16 | type ErrFn = (errMsg: string) => void; 17 | 18 | // ErrTuple is a type that pairs a 'data' field with an 'err' field. Skynet 19 | // libraries typically prefer returning ErrTuples to throwing or rejecting, 20 | // because it allows upstream code to avoid the try/catch/throw pattern. Though 21 | // the pattern is much celebrated in javascript, it encourages relaxed error 22 | // handling, and often makes error handling much more difficult because the try 23 | // and the catch are in different scopes. 24 | // 25 | // Most of the Skynet core libraries do not have any `throws` anywhere in their 26 | // API. 27 | // 28 | // Typically, an ErrTuple will have only one field filled out. If data is 29 | // returned, the err should be 'null'. If an error is returned, the data field 30 | // should generally be empty. Callers are expected to check the error before 31 | // they access any part of the data field. 32 | type ErrTuple = [data: any, err: Err]; 33 | 34 | // KernelAuthStatus is the structure of a message that gets sent by the kernel 35 | // containing its auth status. Auth occurs in 5 stages. 36 | // 37 | // Stage 0; no auth updates 38 | // Stage 1: bootloader is loaded, user is not yet logged in 39 | // Stage 2: bootloader is loaded, user is logged in 40 | // Stage 3: kernel is loaded, user is logged in 41 | // Stage 4: kernel is loaded, user is logging out (refresh iminent) 42 | // 43 | // 'kernelLoaded' is initially set to "not yet" and will be updated when the 44 | // kernel has loaded. If it is set to "success", it means the kernel loaded 45 | // without issues. If it is set to anything else, it means that there was an 46 | // error, and the new value is the error. 47 | // 48 | // 'kernelLoaded' will not be changed until 'loginComplete' has been set to 49 | // true. 'loginComplete' can be set to true immediately if the user is already 50 | // logged in. 51 | // 52 | // 'logoutComplete' can be set to 'true' at any point, which indicates that the 53 | // auth cycle needs to reset. 54 | interface KernelAuthStatus { 55 | loginComplete: boolean; 56 | kernelLoaded: "not yet" | "success" | string; 57 | logoutComplete: boolean; 58 | } 59 | 60 | // SkynetPortal defines the type for a skynet portal. In most cases, the url 61 | // and the name are nearly the same (the URL will have the protocol, and the 62 | // name will not). Sometimes however, they will not be the same. 63 | // 64 | // We need the two fields to be separate because the user will derive things 65 | // like their account pubkey based on the name of the portal, and the portal 66 | // may have to change URLs over time for various reasons. 67 | interface SkynetPortal { 68 | url: string; 69 | name: string; 70 | } 71 | 72 | // RequestOverrideResponse defines the type that the kernel returns as a 73 | // response to a requestOverride call. 74 | interface RequestOverrideResponse { 75 | override: boolean; 76 | headers?: any; // TODO: I don't know how to do an array of types. 77 | body?: Uint8Array; 78 | } 79 | 80 | export { DataFn, ErrFn, Err, ErrTuple, KernelAuthStatus, RequestOverrideResponse, SkynetPortal }; 81 | -------------------------------------------------------------------------------- /libs/libskynet/src/validateObjPropTypes.ts: -------------------------------------------------------------------------------- 1 | import { addContextToErr } from "./err.js"; 2 | import { Err } from "./types.js"; 3 | 4 | // validateObjPropTypes takes an object as input, along with a list of checks 5 | // that should performed on the properties of the object. If all of the 6 | // properties are present in the object and adhere to the suggested types, 7 | // `null` is returned. Otherwise a string is returned indicating the first 8 | // property that failed a check. 9 | // 10 | // This function is intended to be used on objects that were decoded from JSON 11 | // after being received by an untrusted source. 12 | // 13 | // validateObjProperties supports all of the basic types, as well as arrays for 14 | // types boolean, number, bigint, and string. In the future, support for more 15 | // types may be added as well. 16 | // 17 | // Below is an example object, followed by the call that you would make to 18 | // checkObj to verify the object. 19 | // 20 | // const expectedObj = { 21 | // aNum: 35, 22 | // aStr: "hi", 23 | // aBig: 10n, 24 | // aArr: [1, 2, 3], 25 | // }; 26 | // 27 | // const err = validateObjPropTypes(expectedObj, [ 28 | // ["aNum", "number"], 29 | // ["aStr", "string"], 30 | // ["aBig", "bigint"], 31 | // ["aArr", "numberArray"], 32 | // ["aUint8Array", "Uint8Array"], 33 | // ]); 34 | function validateObjPropTypes(obj: any, checks: [string, string][]): Err { 35 | for (let i = 0; i < checks.length; i++) { 36 | const [property, expectedType] = checks[i]; 37 | 38 | // Loop through the array cases. 39 | const arrayCases = [ 40 | ["booleanArray", "boolean"], 41 | ["numberArray", "number"], 42 | ["bigintArray", "bigint"], 43 | ["stringArray", "string"], 44 | ]; 45 | let checkPassed = false; 46 | for (let j = 0; j < arrayCases.length; j++) { 47 | // If this is not an array case, ignore it. 48 | const [arrCaseType, arrType] = arrayCases[j]; 49 | if (expectedType !== arrCaseType) { 50 | continue; 51 | } 52 | 53 | // Check every element in the array. 54 | const err = validateArrayTypes(obj[property], arrType); 55 | if (err !== null) { 56 | return addContextToErr(err, `check failed for array property '${property}'`); 57 | } 58 | 59 | // We found the expected type for this check, we can stop checking the 60 | // rest. 61 | checkPassed = true; 62 | break; 63 | } 64 | // If the type was an array type, we don't need to perform the next check. 65 | if (checkPassed === true) { 66 | continue; 67 | } 68 | 69 | // Uint8Array check. 70 | if (expectedType === "Uint8Array") { 71 | if (obj[property] instanceof Uint8Array) { 72 | continue; 73 | } else { 74 | return `check failed for property '${property};, expecting Uint8Array`; 75 | } 76 | } 77 | 78 | // Generic typeof check. 79 | const type = typeof obj[property]; 80 | if (type !== expectedType) { 81 | return `check failed for property '${property}', expecting ${expectedType} got ${type}`; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | // validateArrayTypes takes an array as input and validates that every element 88 | // in the array matches the provided type. 89 | // 90 | // This is a helper function for validateObjPropTypes, the property is provided 91 | // as an input to produce a more coherent error message. 92 | function validateArrayTypes(arr: any, expectedType: string): Err { 93 | // Check that the provided input is actually an array. 94 | if (!Array.isArray(arr)) { 95 | return `not an array`; 96 | } 97 | for (let i = 0; i < arr.length; i++) { 98 | const type = typeof arr[i]; 99 | if (type !== expectedType) { 100 | return `element ${i} is expected to be ${expectedType}, got ${type}`; 101 | } 102 | } 103 | return null; 104 | } 105 | 106 | export { validateObjPropTypes }; 107 | -------------------------------------------------------------------------------- /libs/libskynet/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./dist" 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/libskynet/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./dist-test" 12 | }, 13 | "include": ["src-test/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/libskynetnode/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /libs/libskynetnode/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-test 3 | node_modules 4 | -------------------------------------------------------------------------------- /libs/libskynetnode/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /libs/libskynetnode/README.md: -------------------------------------------------------------------------------- 1 | ## libskynetnode 2 | 3 | libskynetnode is a node module that offers nodejs compatibility with libskynet. 4 | The vast majority of libskynet functions are isomorphic, but a few of them are 5 | not isomorphic. 6 | 7 | Specifically, any function that requires 'fetch' is not isomorphic (so 8 | progressiveFetch and all helpers that use progressiveFetch), and the random 9 | number generation is also not isomorphic. 10 | -------------------------------------------------------------------------------- /libs/libskynetnode/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("dist", { recursive: true, force: true }); 3 | fs.rmSync("dist-test", { recursive: true, force: true }); 4 | fs.rmSync("node_modules", { recursive: true, force: true }); 5 | -------------------------------------------------------------------------------- /libs/libskynetnode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libskynetnode", 3 | "version": "0.1.4", 4 | "author": "Skynet Labs", 5 | "description": "libksynet, but the non-isomorphic functions are written for node instead of the browser", 6 | "main": "dist/index.js", 7 | "type": "module", 8 | "types": "dist/index.d.js", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "files": [ 13 | "/dist" 14 | ], 15 | "scripts": { 16 | "lint": "prettier -l 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json'", 17 | "clean": "node ./clean.js", 18 | "deps": "npm install libskynet@latest && npm install node-fetch@latest && npm audit fix", 19 | "update-deps": "npm run clean && npm run deps && npm install", 20 | "update-lint": "prettier -w 'src' 'src-test' '*.js' '*.json' && eslint 'src' 'src-test' '*.js' '*.json' --fix", 21 | "update": "npm run update-deps && npm run update-lint", 22 | "test": "tsc && tsc --project tsconfig.test.json && node ./dist-test/test.js", 23 | "build": "npm run clean && npm install && npm run lint && tsc", 24 | "prepublishOnly": "npm run clean && npm install && npm run lint && npm run test && npm run build" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^17.0.23", 28 | "@typescript-eslint/eslint-plugin": "^5.19.0", 29 | "eslint": "^8.13.0", 30 | "prettier": "^2.6.2" 31 | }, 32 | "dependencies": { 33 | "libskynet": "^0.0.63", 34 | "node-fetch": "^3.2.10" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libs/libskynetnode/src-test/test.ts: -------------------------------------------------------------------------------- 1 | console.log("tests have passed"); 2 | 3 | export {}; 4 | -------------------------------------------------------------------------------- /libs/libskynetnode/src/index.ts: -------------------------------------------------------------------------------- 1 | export { overwriteRegistryEntry } from "./registrywrite.js"; 2 | export { generateSeedPhraseRandom } from "./seed.js"; 3 | export { upload } from "./upload.js"; 4 | -------------------------------------------------------------------------------- /libs/libskynetnode/src/registryread.ts: -------------------------------------------------------------------------------- 1 | import { Err, addContextToErr, bufToHex, defaultPortalList, verifyRegistryReadResponse } from "libskynet"; 2 | import { progressiveFetch } from "./progressivefetch.js"; 3 | 4 | // readRegistryEntryResult defines fields that are important to processing a registry 5 | // entry. 6 | interface readRegistryEntryResult { 7 | exists: boolean; 8 | data: Uint8Array; 9 | revision: bigint; 10 | } 11 | 12 | // readRegistryEntry will read and verify a registry entry. The tag strings 13 | // will be hashed with the user's seed to produce the correct entropy. 14 | function readRegistryEntry(pubkey: Uint8Array, datakey: Uint8Array): Promise { 15 | return new Promise((resolve, reject) => { 16 | const pubkeyHex = bufToHex(pubkey); 17 | const datakeyHex = bufToHex(datakey); 18 | const endpoint = "/skynet/registry?publickey=ed25519%3A" + pubkeyHex + "&datakey=" + datakeyHex; 19 | const verifyFunc = function (response: Response): Promise { 20 | return verifyRegistryReadResponse(response, pubkey, datakey); 21 | }; 22 | progressiveFetch(endpoint, {}, defaultPortalList, verifyFunc).then((result: any) => { 23 | // Check for a success. 24 | if (result.success === true) { 25 | result.response 26 | .json() 27 | .then((j: any) => { 28 | resolve({ 29 | exists: true, 30 | data: j.data, 31 | revision: BigInt(j.revision), 32 | }); 33 | }) 34 | .catch((err: any) => { 35 | reject(addContextToErr(err, "unable to parse response despite passing verification")); 36 | }); 37 | return; 38 | } 39 | 40 | // Check for 404. 41 | for (let i = 0; i < result.responsesFailed.length; i++) { 42 | if (result.responsesFailed[i].status === 404) { 43 | resolve({ 44 | exists: false, 45 | data: new Uint8Array(0), 46 | revision: 0n, 47 | }); 48 | return; 49 | } 50 | } 51 | reject("unable to read registry entry\n" + JSON.stringify(result)); 52 | }); 53 | }); 54 | } 55 | 56 | export { readRegistryEntry }; 57 | -------------------------------------------------------------------------------- /libs/libskynetnode/src/seed.ts: -------------------------------------------------------------------------------- 1 | import { randomBytes } from "crypto"; 2 | import { Err, addContextToErr, bufToB64, generateSeedPhraseDeterministic } from "libskynet"; 3 | 4 | // generateSeedPhraseRandom will randomly generate and verify a seed phrase for the user. 5 | function generateSeedPhraseRandom(): [string, Err] { 6 | const buf = Uint8Array.from(randomBytes(32)); 7 | const str = bufToB64(buf); 8 | const [sp, errGSPD] = generateSeedPhraseDeterministic(str); 9 | if (errGSPD !== null) { 10 | return ["", addContextToErr(errGSPD, "unable to generate seed from string")]; 11 | } 12 | return [sp, null]; 13 | } 14 | 15 | export { generateSeedPhraseRandom }; 16 | -------------------------------------------------------------------------------- /libs/libskynetnode/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "outDir": "./dist" 12 | }, 13 | "include": ["src/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/libskynetnode/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true, 10 | "outDir": "./dist-test" 11 | }, 12 | "include": ["src-test/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # Kernel Modules 2 | 3 | This is a collection of modules that are important to kernel development. Some 4 | of them are testing modules, and some of them are core libraries that are 5 | expected to be key dependencies of most applications. 6 | 7 | If you are interested in attempting kernel development yourself, we recommend 8 | checking out the 'template' module, which contains some basic module code and 9 | some instructions for getting started with module development. 10 | -------------------------------------------------------------------------------- /modules/get-set-object/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-empty-functions": "off", 18 | "no-async-promise-executor": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/get-set-object/.gitignore: -------------------------------------------------------------------------------- 1 | # Dev Tools 2 | *.swp 3 | 4 | # Folders created during the build process. 5 | build 6 | dist-build 7 | dist 8 | node_modules 9 | -------------------------------------------------------------------------------- /modules/get-set-object/.module-salt: -------------------------------------------------------------------------------- 1 | dawn eels ablaze drunk drinks bite dusted duties bias debut avatar boat dove quick dawn -------------------------------------------------------------------------------- /modules/get-set-object/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | dist-build 4 | node_modules 5 | -------------------------------------------------------------------------------- /modules/get-set-object/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /modules/get-set-object/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Module Template 2 | 3 | This folder contains the template for creating a new Skynet Kernel module. The 4 | template contains one example API endpoint with documentation, and it contains 5 | build scripts for deploying the module plus instructions for using the build 6 | scripts. 7 | 8 | ## Usage 9 | 10 | ### repeatMessage 11 | 12 | repeatMessage is an example API that repeats an input message back to the 13 | caller. It will prepend a "got message: " string 14 | 15 | ###### Input: 16 | 17 | Provide an input called 'message' to receive a response. 18 | 19 | ```ts 20 | { 21 | module: "", 22 | method: "repeatMessage", 23 | data: { 24 | message: , 25 | }, 26 | } 27 | ``` 28 | 29 | ###### Output: 30 | 31 | ```ts 32 | { 33 | resp: , 34 | } 35 | ``` 36 | 37 | The output contains a field called 'resp' which contains a modified version of 38 | the provided message. 39 | 40 | ## Building and Deploying 41 | 42 | The build and deploy scripts are ready to use. Copy the files, update 43 | `package.json` then run `npm install` and `npm run build` to get started. The 44 | build script will output a resolver skylink that you can use to try out your 45 | module in a testing environment. 46 | 47 | The resolver skylink is unique to each clone of the repsitory. This allows 48 | multiple developers to work on the same module at the same time, and test their 49 | changes in production without stepping on each other's toes. 50 | 51 | To deploy the module to production, run `npm run deploy`. All clones of the 52 | repository will have the **same** resolver skylink for the deploy process, 53 | meaning any developer that knows the deployment password can push changes into 54 | production. 55 | 56 | If the module has never been deployed before, `npm run deploy` will ask you to 57 | set a password. The password should be a secure password (more than 100 bits of 58 | entropy), as anyone who is able to guess the password will be able to deploy 59 | changes to production. 60 | 61 | Modules use a password based deploy system beacuse it allows module deployment 62 | to be fully decentralized. There is no central manager of modules that can make 63 | changes to the code that is in production. This also means there is no way to 64 | reset passwords or perform account recovery if a password is lost. If a 65 | password does get lost, the developer needs to redeploy using a new resolver 66 | skylink for the module, and then needs to tell everyone to update their 67 | depednecies. 68 | -------------------------------------------------------------------------------- /modules/get-set-object/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("build", { recursive: true, force: true }); 3 | fs.rmSync("dist", { recursive: true, force: true }); 4 | fs.rmSync("dist-build", { recursive: true, force: true }); 5 | fs.rmSync("node_modules", { recursive: true, force: true }); 6 | -------------------------------------------------------------------------------- /modules/get-set-object/module-skylink: -------------------------------------------------------------------------------- 1 | AQBsE9Bh_BIG8Byy3Mje7NEyHHA78V_v-Bkr9qB9TdMrlw -------------------------------------------------------------------------------- /modules/get-set-object/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-module-template", 3 | "version": "0.1.0", 4 | "description": "Template for new Kernel module", 5 | "author": "Skynet Labs", 6 | "license": "MIT", 7 | "type": "module", 8 | "main": "index.js", 9 | "scripts": { 10 | "lint": "prettier -l 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json'", 11 | "clean": "node ./clean.js", 12 | "deps": "npm install libkmodule@latest && npm install libskynet@latest && npm audit fix", 13 | "update-deps": "npm run clean && npm run deps && npm install", 14 | "update-lint": "prettier -w 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json' --fix", 15 | "update": "npm run update-deps && npm run update-lint && tsc", 16 | "build-script": "tsc --project tsconfig.build.json", 17 | "compile": "npm run build-script && tsc && rollup -c", 18 | "build": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js dev", 19 | "deploy": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js prod" 20 | }, 21 | "devDependencies": { 22 | "@rollup/plugin-node-resolve": "^13.3.0", 23 | "@types/node": "^18.0.3", 24 | "@types/read": "^0.0.29", 25 | "@typescript-eslint/eslint-plugin": "^5.18.0", 26 | "cpy-cli": "^4.1.0", 27 | "eslint": "^8.13.0", 28 | "libskynetnode": "^0.1.2", 29 | "prettier": "^2.6.2", 30 | "read": "^1.0.7", 31 | "rollup": "^2.75.6", 32 | "rollup-plugin-terser": "^7.0.2", 33 | "typescript": "^4.6.3" 34 | }, 35 | "dependencies": { 36 | "libkmodule": "^0.2.53", 37 | "libskynet": "^0.1.9" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/get-set-object/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import { terser } from "rollup-plugin-terser"; 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | }; 12 | -------------------------------------------------------------------------------- /modules/get-set-object/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/get-set-object/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/.module-salt: -------------------------------------------------------------------------------- 1 | eggs afield bids bailed dwarf cement duets agenda bacon blip amused befit dating framed rhythm -------------------------------------------------------------------------------- /modules/kernel-test-helper/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/README.md: -------------------------------------------------------------------------------- 1 | # kernel-test-helper 2 | 3 | kernel-test-helper is a kernel module that partners with the kernel-test-suite. 4 | It exposes a number of methods that allow the test suite to verify cross-module 5 | communication. 6 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/module-skylink: -------------------------------------------------------------------------------- 1 | AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA 2 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-test-helper-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to facilitate integration testing", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynet": "^0.0.43", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.42" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/kernel-test-helper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/.module-salt: -------------------------------------------------------------------------------- 1 | eggs afield bids bailed dwarf cement duets agenda bacon blip amused befit dating framed rhythm -------------------------------------------------------------------------------- /modules/kernel-test-mysky/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/README.md: -------------------------------------------------------------------------------- 1 | # kernel-test-mysky 2 | 3 | kernel-test-mysky is a test module that receives the mysky seed. Because the 4 | mysky seed holds read-write access to a considerable amount of sensitive user 5 | data, we wanted to put the tests surrounding the myksy seed in its own module. 6 | The kernel only gives the seed to the immutable version of this module. 7 | 8 | By only giving the seed to the immutable version of the module, and by making 9 | sure the total amount of code involved in this test is as tiny as possible, we 10 | minimize the risk that the testing module could be used to expose the user's 11 | mysky root seed. 12 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/module-skylink: -------------------------------------------------------------------------------- 1 | AQCNk243Sqmg0oAlwkAf6bDDT54t2jEh8JNW4k7AaO6ZQQ -------------------------------------------------------------------------------- /modules/kernel-test-mysky/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-test-mysky-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to facilitate integration testing", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynet": "^0.0.43", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.11" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/src/index.ts: -------------------------------------------------------------------------------- 1 | import { addContextToErr, addHandler, activeQuery, handleMessage } from "libkmodule" 2 | import { dataFn, ed25519Keypair, ed25519Sign, ed25519Verify, error } from "libskynet" 3 | 4 | // Track any errors that come up during execution. This is non-standard, most 5 | // modules will not need to do this. 6 | let errors: string[] = [] 7 | 8 | // Establish a promise to receive the mysky root. 9 | let myskyRootKeypairResolve: dataFn 10 | let myskyRootKeypairPromise: Promise<[ed25519Keypair, error]> = new Promise((resolve) => { 11 | myskyRootKeypairResolve = resolve 12 | }) 13 | 14 | // Establish 'onmessage' for the worker, we'll just be using the libkmodule 15 | // method 'handleMessage' nakedly. 16 | onmessage = function (event: MessageEvent) { 17 | // Have a special handler to look for the mysky root - this handler is not 18 | // provided by libkmodule by default. Check the presentSeed message for the 19 | // myskyRootKeypair, otherwise just let the message pass through to 20 | // handleMessage so that everything else can be handled as well. 21 | if (event.data.method === "presentSeed") { 22 | if (event.data.data.myskyRootKeypair === undefined) { 23 | myskyRootKeypairResolve([{}, "did not receive the myskyRootKeypair"]) 24 | errors.push("did not receive myskyRootKeypair") 25 | } else { 26 | myskyRootKeypairResolve([event.data.data.myskyRootKeypair, null]) 27 | } 28 | } 29 | handleMessage(event) 30 | } 31 | 32 | // handleConfirmMyskyRoot is a function that will confirm it has received the 33 | // mysky root. 34 | // 35 | // NOTE: Because this module receives the mysky root, we have to be careful not 36 | // to reveal that root as other untrusted modules can call this module. 37 | function handleConfirmMyskyRoot(aq: activeQuery) { 38 | myskyRootKeypairPromise.then(([keypair, err]) => { 39 | // Check that the keypair was recovered without error. 40 | if (err !== null) { 41 | aq.reject(addContextToErr(err, "did not get the mysky root keypair")) 42 | errors.push(err) 43 | return 44 | } 45 | 46 | // Try doing a sign and verify with the keypair. 47 | let msg = new TextEncoder().encode("test msg") 48 | let [sig, errSign] = ed25519Sign(msg, keypair.secretKey) 49 | if (errSign !== null) { 50 | aq.reject(addContextToErr(errSign, "could not sign a message with the keypair")) 51 | errors.push("could not sign keypair") 52 | return 53 | } 54 | if (ed25519Verify(msg, sig, keypair.publicKey) !== true) { 55 | aq.reject("signature verification failed") 56 | errors.push("signature verifiaction failed") 57 | return 58 | } 59 | 60 | // Establish that the myksy root is a working key. We can't really do 61 | // more than that in this test, we just want to make sure the kernel is 62 | // getting a key. 63 | aq.respond("success, we appear to have a real keypair") 64 | }) 65 | } 66 | addHandler("confirmMyskyRoot", handleConfirmMyskyRoot) 67 | 68 | // handleViewErrors exposes the errors object that accumulates all the errors 69 | // the module finds throughout testing. 70 | function handleViewErrors(aq: activeQuery) { 71 | aq.respond({ errors }) 72 | } 73 | addHandler("viewErrors", handleViewErrors) 74 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/kernel-test-mysky/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/.module-salt: -------------------------------------------------------------------------------- 1 | bakery avatar coffee basin afar amidst copy duties dented dogs dove comb auburn vortex bamboo -------------------------------------------------------------------------------- /modules/kernel-test-suite/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/README.md: -------------------------------------------------------------------------------- 1 | # kernel-test-suite 2 | 3 | kernel-test-suite is a kernel module that provides a set of functions designed 4 | to facilitate integration testing with the kernel. Some of the methods test 5 | that basic operations succeed, and other methods are intentionally looking for 6 | the kernel to return an error. The kernel-test-suite depends on a few helper 7 | modules, especially for checking that cross-module communication works. 8 | 9 | There are a good number of comments throughout the module, someone who is 10 | looking to learn a bit more about module development or about how the kernel 11 | works in general could get a lot of value out of reading the code. It should be 12 | noted that because this is a tester module, there are a lot more checks and a 13 | lot more input validation than is typically necessary in an ordinary module. We 14 | tried to mark the places where this is happening, but it may also be good to 15 | check out a non-testing module as well to see what a standard module looks 16 | like. 17 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/module-skylink: -------------------------------------------------------------------------------- 1 | AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g 2 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-test-suite-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to test the kernel's APIs", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynet": "^0.0.43", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.42" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/kernel-test-suite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-download/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/secure-download/.module-salt: -------------------------------------------------------------------------------- 1 | birth dreams cent audio circle aplomb banjo clue afraid aglow dodge ammo dodge cake jabbed -------------------------------------------------------------------------------- /modules/secure-download/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/secure-download/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/secure-download/README.md: -------------------------------------------------------------------------------- 1 | # secure-download 2 | 3 | secure-download is a kernel module that provides downloading functionality. The 4 | data that is received from the portal is verified before being served to the 5 | user. If the portal tries to cheat or returns a file that does not match the 6 | hash, the module will try another portal or return an error. 7 | 8 | NOTE: secure-download currently only handles files that fully fit into a base 9 | sector. 10 | 11 | secure-download is a fully trustless module. 12 | 13 | ## Usage 14 | 15 | #### secureDownload 16 | 17 | Input: 18 | 19 | Provide the skylink of the file you are downloading. 20 | 21 | ```ts 22 | { 23 | module: "AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw", 24 | method: "secureDownload", 25 | data: { 26 | skylink: , 27 | }, 28 | } 29 | ``` 30 | 31 | Output: 32 | 33 | ```ts 34 | { 35 | fileData: , 36 | } 37 | ``` 38 | 39 | Currently only the fileData is returned. At some point we may add metadata 40 | fields to the return value as well. 41 | -------------------------------------------------------------------------------- /modules/secure-download/module-skylink: -------------------------------------------------------------------------------- 1 | AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw 2 | -------------------------------------------------------------------------------- /modules/secure-download/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-download-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to securely download files from skynet", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest && npm install libskynet@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynet": "^0.0.57", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.38" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/secure-download/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-download/src/index.ts: -------------------------------------------------------------------------------- 1 | // secure-download is a module which will download a file from Skynet. The hash 2 | // of the file is computed locally after the data is received to ensure that 3 | // the data matches the skylink. 4 | 5 | import { ActiveQuery, addContextToErr, addHandler, handleMessage, objAsString } from "libkmodule" 6 | 7 | import { 8 | b64ToBuf, 9 | defaultPortalList, 10 | error, 11 | progressiveFetch, 12 | progressiveFetchResult, 13 | validSkylink, 14 | verifyDownloadResponse, 15 | } from "libskynet" 16 | 17 | addHandler("secureDownload", handleSecureDownload) 18 | onmessage = handleMessage 19 | 20 | // handleSecureDownload will handle a call to secureDownload. 21 | function handleSecureDownload(aq: ActiveQuery) { 22 | // Check the inputs. 23 | if (typeof aq.callerInput.skylink !== "string") { 24 | aq.reject("filename is expected to be a string") 25 | return 26 | } 27 | 28 | // Parse the skylink into a uint8array and ensure it is valid. 29 | let [u8Link, err64] = b64ToBuf(aq.callerInput.skylink) 30 | if (err64 !== null) { 31 | aq.reject(addContextToErr(err64, "unable to decode skylink")) 32 | return 33 | } 34 | if (!validSkylink(u8Link)) { 35 | aq.reject("skylink " + aq.callerInput.skylink + " is not valid") 36 | return 37 | } 38 | 39 | // Call progressiveFetch to perform the download. 40 | let endpoint = "/skynet/trustless/basesector/" + aq.callerInput.skylink 41 | let fileDataPtr = { fileData: new Uint8Array(0), err: null } 42 | let verify = function (response: Response): Promise { 43 | return verifyDownloadResponse(response, u8Link, fileDataPtr) 44 | } 45 | progressiveFetch(endpoint, null, defaultPortalList, verify).then((result: progressiveFetchResult) => { 46 | if (result.success !== true) { 47 | let err = objAsString({ logs: result.logs }) 48 | aq.reject(addContextToErr(err, "unable to download file")) 49 | return 50 | } 51 | if (fileDataPtr.err !== null) { 52 | aq.reject(addContextToErr(fileDataPtr.err, "file appears to be corrupt")) 53 | return 54 | } 55 | aq.respond({ 56 | fileData: fileDataPtr.fileData, 57 | }) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /modules/secure-download/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/secure-download/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-registry/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/secure-registry/.module-salt: -------------------------------------------------------------------------------- 1 | agony aztec eating dying baby dilute dodge dash aglow among army alarms bays amaze abyss -------------------------------------------------------------------------------- /modules/secure-registry/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/secure-registry/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/secure-registry/README.md: -------------------------------------------------------------------------------- 1 | # secure-registry 2 | 3 | secure-registry is a kernel module that enables interaction with the registry. 4 | All registry entries that are provided are verified cryptographically, which 5 | eliminates several types of abuse that the portal can commit against the user. 6 | 7 | ## Usage 8 | 9 | #### readEntry 10 | 11 | Input: 12 | 13 | Provide the public key and the data key of the registry entry you wish to read. 14 | 15 | ```ts 16 | { 17 | module: "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q", 18 | method: "readEntry", 19 | data: { 20 | publicKey: , 21 | dataKey: , 22 | }, 23 | } 24 | ``` 25 | 26 | Output: 27 | 28 | ```ts 29 | { 30 | exists: , 31 | deleted?: , 32 | entryData?: , 33 | revision?: , 34 | } 35 | ``` 36 | 37 | If the entry does not exist, the 'exists' field of the output will be set to 38 | false. This field should always be checked first. If the entry does not exist, 39 | the other fields will be omitted. 40 | 41 | 'deleted' will be set to true if the registry entry exists, but currently has a 42 | value that indicates the data was deleted. If 'deleted' is set to true, a 43 | revision number will be provided, but no entryData will be provided. 44 | 45 | If the entry exists and has not been deleted, 'entryData' will contain the 46 | binary contents of the registry entry. 47 | 48 | #### writeEntry 49 | 50 | writeEntry will write a new registry entry using a provided revision number. 51 | writeEntry is potentially unsafe, the caller should ensure that they have set a 52 | revision number that matches the most recent data that they have read. 53 | 54 | It is generally not recommended to use writeEntry directly. Instead, callers 55 | should use a library that wraps writeEntry with safety mechanisms. 56 | 57 | Input: 58 | 59 | The inputs include a keypair, the data key, the data itself, and a revision 60 | number. The module will perform the signing and uploading. 61 | 62 | ```ts 63 | { 64 | module: "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q", 65 | method: "writeEntry", 66 | data: { 67 | publicKey: , 68 | secretKey: , 69 | dataKey: , 70 | entryData: , 71 | revision: , 72 | }, 73 | } 74 | ``` 75 | 76 | Output: 77 | 78 | ```ts 79 | { 80 | entryID: , 81 | } 82 | ``` 83 | 84 | The output is the entryID of the registry entry that got written. 85 | 86 | ## Roadmap 87 | 88 | ###### Add support for getsetjson 89 | 90 | ###### Add support for subscriptions 91 | 92 | ###### Add cost and performance optimizations 93 | -------------------------------------------------------------------------------- /modules/secure-registry/module-skylink: -------------------------------------------------------------------------------- 1 | AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q 2 | -------------------------------------------------------------------------------- /modules/secure-registry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-registry-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to securely interact with the registry", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest && npm install libskynet@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynetnode": "^0.1.2", 26 | "prettier": "^2.6.2", 27 | "read": "^1.0.7", 28 | "rollup": "^2.75.6", 29 | "rollup-plugin-terser": "^7.0.2", 30 | "typescript": "^4.6.3" 31 | }, 32 | "dependencies": { 33 | "libkmodule": "^0.2.14", 34 | "libskynet": "^0.0.50" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/secure-registry/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-registry/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/secure-registry/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-upload/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "prefer-const": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/secure-upload/.module-salt: -------------------------------------------------------------------------------- 1 | auburn drunk amused beyond archer boss boat cage bays afield ablaze camp cowl morsel drinks -------------------------------------------------------------------------------- /modules/secure-upload/.prettierignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | -------------------------------------------------------------------------------- /modules/secure-upload/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /modules/secure-upload/README.md: -------------------------------------------------------------------------------- 1 | # secure-upload 2 | 3 | secure-upload is a kernel module that provides uploading functionality. The 4 | final skylink of the file is computed before uploading to the kernel, which 5 | means that the user is guaranteed that the data in the skylink matches the 6 | intended upload, the portal cannot cheat here. 7 | 8 | secure-upload does trust the portal to properly custody the file on Sia. If the 9 | portal decides to delete the file, the file may unexpectedly become unavailable 10 | on Skynet. The portal cannot however modify the data, any modifcations will be 11 | ignored. 12 | 13 | ## Usage 14 | 15 | #### secureUpload 16 | 17 | Input: 18 | 19 | Provide the name of the file you are uploading and the data for the file. 20 | Directory uploads are not supported by the secureUpload call. 21 | 22 | ```ts 23 | { 24 | module: "AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg", 25 | method: "secureUpload", 26 | data: { 27 | filename: , 28 | fileData: , 29 | }, 30 | } 31 | ``` 32 | 33 | Output: 34 | 35 | ```ts 36 | { 37 | skylink: , 38 | } 39 | ``` 40 | 41 | The skylink that gets returned is the skylink of the file that was uploaded. 42 | secureUpload will verify the skylink locally, ensuring that the user receives a 43 | trustworthy skylink. 44 | -------------------------------------------------------------------------------- /modules/secure-upload/module-skylink: -------------------------------------------------------------------------------- 1 | AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg 2 | -------------------------------------------------------------------------------- /modules/secure-upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-upload-module", 3 | "version": "0.1.0", 4 | "description": "skynet kernel module to securely upload files to skynet", 5 | "main": "index.js", 6 | "scripts": { 7 | "deps": "npm install libkmodule@latest && npm install libskynet@latest", 8 | "prettier": "prettier --write .", 9 | "eslint": "eslint src --fix", 10 | "lint": "npm run deps && npm run prettier && npm run eslint", 11 | "build-script": "tsc --project tsconfig.build.json", 12 | "compile": "npm run build-script && tsc && rollup -c", 13 | "build": "npm run compile && node ./dist-build/build.js dev", 14 | "deploy": "npm run compile && node ./dist-build/build.js prod" 15 | }, 16 | "author": "David Vorick", 17 | "license": "MIT", 18 | "type": "module", 19 | "devDependencies": { 20 | "@rollup/plugin-node-resolve": "^13.3.0", 21 | "@types/read": "^0.0.29", 22 | "@typescript-eslint/eslint-plugin": "^5.18.0", 23 | "cpy-cli": "^4.1.0", 24 | "eslint": "^8.13.0", 25 | "libskynet": "^0.0.48", 26 | "libskynetnode": "^0.1.2", 27 | "prettier": "^2.6.2", 28 | "read": "^1.0.7", 29 | "rollup": "^2.75.6", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "dependencies": { 34 | "libkmodule": "^0.2.11" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /modules/secure-upload/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve" 2 | import { terser } from "rollup-plugin-terser" 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | } 12 | -------------------------------------------------------------------------------- /modules/secure-upload/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/secure-upload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/skynet-portal/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-empty-functions": "off", 18 | "no-async-promise-executor": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/skynet-portal/.gitignore: -------------------------------------------------------------------------------- 1 | # Dev Tools 2 | *.swp 3 | 4 | # Folders created during the build process. 5 | build 6 | dist-build 7 | dist 8 | node_modules 9 | -------------------------------------------------------------------------------- /modules/skynet-portal/.module-salt: -------------------------------------------------------------------------------- 1 | duets debut auburn ankle dented cake coils eden bypass avatar circle donuts dime eden orchid -------------------------------------------------------------------------------- /modules/skynet-portal/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | dist-build 4 | node_modules 5 | -------------------------------------------------------------------------------- /modules/skynet-portal/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /modules/skynet-portal/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Portal Module 2 | 3 | The Skynet Portal module is the main module for connecting applications to 4 | Skynet. It tracks all of a user's accounts with portals, tracks the user's 5 | spending habits, sets ratelimits, manages performance and failover, and overall 6 | provides all of the heavy lifting for ensuring a relaible and performant 7 | experience on Skynet. 8 | 9 | ## Usage 10 | 11 | 12 | -------------------------------------------------------------------------------- /modules/skynet-portal/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("build", { recursive: true, force: true }); 3 | fs.rmSync("dist", { recursive: true, force: true }); 4 | fs.rmSync("dist-build", { recursive: true, force: true }); 5 | fs.rmSync("node_modules", { recursive: true, force: true }); 6 | -------------------------------------------------------------------------------- /modules/skynet-portal/module-skylink: -------------------------------------------------------------------------------- 1 | AQCBPFvXNvdtnLbWCRhC5WKhLxxXlel-EDwNM7-GQ-XV3Q -------------------------------------------------------------------------------- /modules/skynet-portal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-module-template", 3 | "version": "0.1.0", 4 | "description": "Template for new Kernel module", 5 | "author": "Skynet Labs", 6 | "license": "MIT", 7 | "type": "module", 8 | "main": "index.js", 9 | "scripts": { 10 | "lint": "prettier -l 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json'", 11 | "clean": "node ./clean.js", 12 | "deps": "npm install libkmodule@latest && npm install libskynet@latest && npm audit fix", 13 | "update-deps": "npm run clean && npm run deps && npm install", 14 | "update-lint": "prettier -w 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json' --fix", 15 | "update": "npm run update-deps && npm run update-lint && tsc", 16 | "compile": "npm run build-script && tsc && rollup -c", 17 | "build-script": "tsc --project tsconfig.build.json", 18 | "build": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js dev", 19 | "deploy": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js prod" 20 | }, 21 | "devDependencies": { 22 | "@rollup/plugin-node-resolve": "^13.3.0", 23 | "@types/node": "^18.0.3", 24 | "@types/read": "^0.0.29", 25 | "@typescript-eslint/eslint-plugin": "^5.18.0", 26 | "cpy-cli": "^4.1.0", 27 | "eslint": "^8.13.0", 28 | "libskynet": "^0.1.7", 29 | "libskynetnode": "^0.1.2", 30 | "prettier": "^2.6.2", 31 | "read": "^1.0.7", 32 | "rollup": "^2.75.6", 33 | "rollup-plugin-terser": "^7.0.2", 34 | "typescript": "^4.6.3" 35 | }, 36 | "dependencies": { 37 | "libkmodule": "^0.2.51" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/skynet-portal/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import { terser } from "rollup-plugin-terser"; 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | }; 12 | -------------------------------------------------------------------------------- /modules/skynet-portal/src/index.ts: -------------------------------------------------------------------------------- 1 | import { blockForInit, init, viewPortalConnections } from "./init.js"; 2 | import { PortalConnectionTest, testPortalConnection } from "./testPortalConnections.js"; 3 | import { ActiveQuery, addContextToErr, addHandler, handleMessage, logErr, objAsString } from "libkmodule"; 4 | 5 | // Required for libkmodule to work. 6 | onmessage = handleMessage; 7 | 8 | // Establish all of the handlers for the various methods that are supported by 9 | // the module. 10 | addHandler("viewPortalConnections", handleViewPortalConnections); 11 | addHandler("testPortalConnections", handleTestPortalConnections); 12 | 13 | // Initialize the module. Portals cannot be used until the promise returned by 14 | // 'blockForInit' is resolving. 15 | init(); 16 | 17 | // handleViewPortalConnections returns the list of active portal connections. 18 | async function handleViewPortalConnections(aq: ActiveQuery): Promise { 19 | // Let the module finish initializing and then return the list of 20 | // portalConnection objects. 21 | const connectErr = await blockForInit(); 22 | aq.respond({ 23 | connectErr, 24 | portalConnections: viewPortalConnections(), 25 | }); 26 | } 27 | 28 | // handleTestPortalConnections will hit several endpoints on each portal, and 29 | // return a diagnostic list of every portal that was hit and what endpoints 30 | // appear to be available. 31 | async function handleTestPortalConnections(aq: ActiveQuery): Promise { 32 | // Wait for startup to complete. 33 | const connectErr = await blockForInit(); 34 | if (connectErr !== null) { 35 | aq.reject(addContextToErr(connectErr, "unable to connect to a portal")); 36 | return; 37 | } 38 | 39 | // Run the test on each portal. 40 | // 41 | // TODO: Need to switch to using Promise.all() but I couldn't get it working 42 | // in time for this commit. 43 | // const testPromises: Promise[] = []; 44 | const portalConnections = viewPortalConnections(); 45 | const testResults: PortalConnectionTest[] = []; 46 | for (let i = 0; i < portalConnections.length; i++) { 47 | const portal = portalConnections[i].portal; 48 | const result = await testPortalConnection(portal); 49 | testResults.push(result); 50 | // testPromises.push(testPortalConnection(portal)); 51 | } 52 | // const testResults = Promise.all(testPromises); 53 | aq.respond(testResults); 54 | 55 | /* 56 | const portalConnection = portalConnections[0]; 57 | const testFile = "bAAN61ryDqNgskNqhHn2YVXWtQ6CG3Xf_gjoB6JB83u8Dg"; 58 | const query = portalConnection.portal.url + "/" + testFile; 59 | const fetchOpts = { 60 | headers: { 61 | Authorization: "Bearer " + portalConnection.authToken, 62 | }, 63 | }; 64 | fetch(query, fetchOpts) 65 | .then((response: Response) => { 66 | logErr("test logged in status:", response.status); 67 | if (response.status !== 200) { 68 | aq.reject("got a bad status"); 69 | return; 70 | } 71 | aq.respond("download seems to have succeeded"); 72 | }) 73 | .catch((err: any) => { 74 | aq.reject(addContextToErr(objAsString(err), "fetch request failed")); 75 | }); 76 | */ 77 | } 78 | -------------------------------------------------------------------------------- /modules/skynet-portal/src/registrySubscriptions.ts: -------------------------------------------------------------------------------- 1 | // registrySubscriptions.ts handles the registry subscription protocol, 2 | // including creating the websocket and selecting registry entries to listen 3 | // to. 4 | 5 | // TODO: Need to update the kernel so that it can route responses to webapps. 6 | // Also need to update the kernel so that it can kill connections and 7 | // subscriptions when a webapp has been closed. All related modules should be 8 | // destroyed. 9 | 10 | // RegistrySubscription defines the expected input data when subscribing to a 11 | // registry entry. The inputs are the public key and the data key of the 12 | // registry entry that we are listening to, and a method that should be called 13 | // when an update is received. 14 | // 15 | // When a registry entry is updated, the original caller will be called using 16 | // the callbackMethod. The data provided as input to the callbackMethod will be 17 | // a RegistryEntryUpdate. 18 | interface RegistryEntrySubscription { 19 | publicKey: Uint8Array; 20 | dataKey: Uint8Array; 21 | callbackMethod: string; 22 | } 23 | 24 | // RegistryEntryUpdate defines the object that will be provided in the callback 25 | // when a registry entry is updated. If 'deleted' is set to 'true', it means 26 | // the registry entry was deleted, and the entryData will be an empty array. 27 | interface RegistryEntryUpdate { 28 | id: Uint8Array; 29 | deleted: boolean; 30 | entryData: Uint8Array; 31 | revision: bigint; 32 | } 33 | 34 | // RegistrySubscriptionTracker tracks the latest update for a registry entry, 35 | // as well as all of the active subscriptions to that registry entry. 36 | interface RegistrySubscriptionTracker { 37 | LatestRevision: RegistryEntryUpdate; 38 | ActiveSubscriptions: ActiveSubscription[]; 39 | } 40 | 41 | // ActiveSubscription defines the data that is used to track a module that 42 | // subscribed to a registry entry. There can be multiple ActiveSubscriptions 43 | // per module, so long as each one has a different callbackMethod. 44 | interface ActiveSubscription { 45 | domain: string; 46 | callbackMethod: string; 47 | } 48 | 49 | // subscriptionTracker is a hashmap that maps from a registry entry id to the 50 | // list of ActiveSubscriptions for that registry entry. When a registry entry 51 | // update is received, the callback is called on every active subscription 52 | const subscriptionTracker = {}; 53 | 54 | // createSubscriptionConnection will create a registry subscription connection 55 | // to a portal. 56 | // 57 | // NOTE: You can't create a websocket connection using new WebSocket, because 58 | // the connection can't authorize using an auth token that way. Instead you 59 | // have to create an https connection and pass in upgrade headers. 60 | async function createSubscriptionConnection( 61 | callerDomain: string, 62 | subRequest: RegistryEntrySubscription 63 | ): Promise { 64 | // TODO: Get the registry entry id of the subRequest. 65 | // 66 | // TODO: Check if we are already subscribed 67 | // 68 | // TODO: If so, add a new activeSubscription and return the latest data 69 | // 70 | // TODO: If not, subscribe to the registry entry and add an active subscription 71 | } 72 | -------------------------------------------------------------------------------- /modules/skynet-portal/src/testPortalConnections.ts: -------------------------------------------------------------------------------- 1 | // testPortalConnections.ts defines the test that will check which portals are 2 | // connectable and which endpoints are working on each portal. 3 | 4 | import { SkynetPortal } from "libskynet"; 5 | 6 | // PortalConnectionTest defines the object that gets filled out after a portal 7 | // connection test has completed. 8 | interface PortalConnectionTest { 9 | portal: SkynetPortal; 10 | 11 | // We will add a field for each endpoint, and potentially metrics 12 | // information. 13 | } 14 | 15 | // testPortalConnection runs tests on a portal and reports which endpoints 16 | // appear to be working. 17 | async function testPortalConnection(portal: SkynetPortal): Promise { 18 | return new Promise((resolve) => { 19 | resolve({ 20 | portal, 21 | }); 22 | }); 23 | } 24 | 25 | export { PortalConnectionTest, testPortalConnection }; 26 | -------------------------------------------------------------------------------- /modules/skynet-portal/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/skynet-portal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /modules/template/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { 10 | "ecmaVersion": "latest", 11 | "sourceType": "module" 12 | }, 13 | "plugins": ["@typescript-eslint"], 14 | "rules": { 15 | "@typescript-eslint/no-explicit-any": "off", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "@typescript-eslint/no-empty-functions": "off", 18 | "no-async-promise-executor": "off" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/template/.gitignore: -------------------------------------------------------------------------------- 1 | # Dev Tools 2 | *.swp 3 | 4 | # Folders created during the build process. 5 | build 6 | dist-build 7 | dist 8 | node_modules 9 | -------------------------------------------------------------------------------- /modules/template/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | dist-build 4 | node_modules 5 | -------------------------------------------------------------------------------- /modules/template/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /modules/template/README.md: -------------------------------------------------------------------------------- 1 | # Skynet Module Template 2 | 3 | This folder contains the template for creating a new Skynet Kernel module. The 4 | template contains one example API endpoint with documentation, and it contains 5 | build scripts for deploying the module plus instructions for using the build 6 | scripts. 7 | 8 | ## Usage 9 | 10 | ### repeatMessage 11 | 12 | repeatMessage is an example API that repeats an input message back to the 13 | caller. It will prepend a "got message: " string 14 | 15 | ###### Input: 16 | 17 | Provide an input called 'message' to receive a response. 18 | 19 | ```ts 20 | { 21 | module: "", 22 | method: "repeatMessage", 23 | data: { 24 | message: , 25 | }, 26 | } 27 | ``` 28 | 29 | ###### Output: 30 | 31 | ```ts 32 | { 33 | resp: , 34 | } 35 | ``` 36 | 37 | The output contains a field called 'resp' which contains a modified version of 38 | the provided message. 39 | 40 | ## Building and Deploying 41 | 42 | The build and deploy scripts are ready to use. Copy the files, update 43 | `package.json` then run `npm install` and `npm run build` to get started. The 44 | build script will output a resolver skylink that you can use to try out your 45 | module in a testing environment. 46 | 47 | The resolver skylink is unique to each clone of the repsitory. This allows 48 | multiple developers to work on the same module at the same time, and test their 49 | changes in production without stepping on each other's toes. 50 | 51 | To deploy the module to production, run `npm run deploy`. All clones of the 52 | repository will have the **same** resolver skylink for the deploy process, 53 | meaning any developer that knows the deployment password can push changes into 54 | production. 55 | 56 | If the module has never been deployed before, `npm run deploy` will ask you to 57 | set a password. The password should be a secure password (more than 100 bits of 58 | entropy), as anyone who is able to guess the password will be able to deploy 59 | changes to production. 60 | 61 | Modules use a password based deploy system beacuse it allows module deployment 62 | to be fully decentralized. There is no central manager of modules that can make 63 | changes to the code that is in production. This also means there is no way to 64 | reset passwords or perform account recovery if a password is lost. If a 65 | password does get lost, the developer needs to redeploy using a new resolver 66 | skylink for the module, and then needs to tell everyone to update their 67 | depednecies. 68 | 69 | ## Best Practices 70 | 71 | One should avoid sending more than one 'response' message. If you are using 72 | libkmodule, this means you should take care to only make one call to either 73 | aq.reject or aq.respond. To minimize the chance of sending two responses, 74 | responses should only be made in functions with a the prefix 'handle'. If 75 | within the 'handle' function you call another function that is expected to 76 | provide a response, that function should also have the 'handle' prefix. This 77 | makes it easier for code reviewers (and linters) to verify that your code is 78 | not at risk of sending two responses. 79 | 80 | One should define a new type in Typescript for every message that gets sent or 81 | received in a module's API. Especially when development is rapid, documentation 82 | can fall behind. If everything is typed, the type system can add least add 83 | another layer of readability to the documentation, and reduce errors caused by 84 | the sender and receiver being out of sync around the latest version of a type. 85 | -------------------------------------------------------------------------------- /modules/template/clean.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | fs.rmSync("build", { recursive: true, force: true }); 3 | fs.rmSync("dist", { recursive: true, force: true }); 4 | fs.rmSync("dist-build", { recursive: true, force: true }); 5 | fs.rmSync("node_modules", { recursive: true, force: true }); 6 | -------------------------------------------------------------------------------- /modules/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kernel-module-template", 3 | "version": "0.1.0", 4 | "description": "Template for new Kernel module", 5 | "author": "Skynet Labs", 6 | "license": "MIT", 7 | "type": "module", 8 | "main": "index.js", 9 | "scripts": { 10 | "lint": "prettier -l 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json'", 11 | "clean": "node ./clean.js", 12 | "deps": "npm install libkmodule@latest && npm audit fix", 13 | "update-deps": "npm run clean && npm run deps && npm install", 14 | "update-lint": "prettier -w 'src' 'src-build' '*.js' '*.json' && eslint 'src' 'src-build' '*.js' '*.json' --fix", 15 | "update": "npm run update-deps && npm run update-lint && tsc", 16 | "build-script": "tsc --project tsconfig.build.json", 17 | "compile": "npm run build-script && tsc && rollup -c", 18 | "build": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js dev", 19 | "deploy": "npm run clean && npm install && npm run lint && npm run compile && node ./dist-build/build.js prod" 20 | }, 21 | "devDependencies": { 22 | "@rollup/plugin-node-resolve": "^13.3.0", 23 | "@types/node": "^18.0.3", 24 | "@types/read": "^0.0.29", 25 | "@typescript-eslint/eslint-plugin": "^5.18.0", 26 | "cpy-cli": "^4.1.0", 27 | "eslint": "^8.13.0", 28 | "libskynet": "^0.0.43", 29 | "libskynetnode": "^0.1.2", 30 | "prettier": "^2.6.2", 31 | "read": "^1.0.7", 32 | "rollup": "^2.75.6", 33 | "rollup-plugin-terser": "^7.0.2", 34 | "typescript": "^4.6.3" 35 | }, 36 | "dependencies": { 37 | "libkmodule": "^0.2.48" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/template/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import { terser } from "rollup-plugin-terser"; 3 | 4 | export default { 5 | input: "build/index.js", 6 | output: { 7 | file: "dist/index.js", 8 | format: "cjs", 9 | }, 10 | plugins: [resolve(), terser()], 11 | }; 12 | -------------------------------------------------------------------------------- /modules/template/src/index.ts: -------------------------------------------------------------------------------- 1 | import { addHandler, handleMessage } from "libkmodule"; 2 | import type { ActiveQuery } from "libkmodule"; 3 | 4 | // Required for libkmodule to work. 5 | onmessage = handleMessage; 6 | 7 | // Will route calls to "repeatMessage" to handleRepeatMessage. 8 | addHandler("repeatMessage", handleRepeatMessage); 9 | 10 | // handleRepeatMessage will handle a call to repeatMessage. 11 | function handleRepeatMessage(aq: ActiveQuery) { 12 | // We are expecting there to be one input field 13 | // 'message' which has a string value. 14 | if (typeof aq.callerInput.message !== "string") { 15 | aq.reject("Field `message` expected to be a string"); 16 | return; 17 | } 18 | 19 | // Respond to query with data 20 | aq.respond({ resp: "got message: " + aq.callerInput.message }); 21 | } 22 | -------------------------------------------------------------------------------- /modules/template/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "declaration": true, 8 | "outDir": "./dist-build", 9 | "strict": true 10 | }, 11 | "include": ["src-build"], 12 | "exclude": ["node_modules", "**/__tests__/*"] 13 | } 14 | -------------------------------------------------------------------------------- /modules/template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "declaration": true, 5 | "moduleResolution": "node", 6 | "outDir": "./build", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } 12 | -------------------------------------------------------------------------------- /webapps/kernel-dev-dashboard/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | !public 15 | .DS_Store 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /webapps/kernel-dev-dashboard/README.md: -------------------------------------------------------------------------------- 1 | # Kernel Dev Dashboard 2 | -------------------------------------------------------------------------------- /webapps/kernel-dev-dashboard/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 13 | // "public path" at which the app is served. 14 | // webpack needs to know it to put the right