├── packages ├── at_repl │ ├── doc │ │ └── api │ │ │ ├── categories.json │ │ │ ├── static-assets │ │ │ ├── favicon.png │ │ │ ├── play_button.svg │ │ │ ├── readme.md │ │ │ ├── search.svg │ │ │ ├── github.css │ │ │ └── styles.css │ │ │ ├── index.json │ │ │ ├── search.html │ │ │ ├── __404error.html │ │ │ ├── at_repl │ │ │ └── at_repl-library.html │ │ │ └── index.html │ ├── lib │ │ ├── version.dart │ │ ├── repl_mode.dart │ │ ├── repl_exception.dart │ │ ├── constants.dart │ │ ├── interactive_session.dart │ │ ├── features │ │ │ ├── delete.dart │ │ │ ├── get.dart │ │ │ ├── put.dart │ │ │ ├── scan.dart │ │ │ ├── monitor.dart │ │ │ ├── help.dart │ │ │ ├── inspect_keys.dart │ │ │ └── inspect_notifications.dart │ │ └── repl.dart │ ├── .gitignore │ ├── example │ │ ├── image.png │ │ ├── local.gif │ │ ├── usage.gif │ │ └── example.md │ ├── CHANGELOG.md │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── LICENSE │ ├── README.md │ ├── bin │ │ └── at_repl.dart │ └── decisions │ │ └── repl.md ├── at_pkam │ ├── lib │ │ ├── at_pkam.dart │ │ ├── pkam_constants.dart │ │ └── commandline_parser.dart │ ├── test │ │ └── at_pkam_test.dart │ ├── .gitignore │ ├── CHANGELOG.md │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── bin │ │ ├── at_pkam.dart │ │ └── main.dart │ ├── LICENSE │ └── README.md ├── at_ve_doctor │ ├── lib │ │ └── at_ve_doctor.dart │ ├── CHANGELOG.md │ ├── .gitignore │ ├── test │ │ └── at_ve_doctor_test.dart │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── bin │ │ └── at_ve_doctor.dart │ └── README.md ├── at_hive_recovery │ ├── CHANGELOG.md │ ├── pubspec.yaml │ ├── README.md │ ├── bin │ │ └── main.dart │ ├── .gitignore │ └── lib │ │ └── src │ │ └── hive_doctor.dart ├── at_cram │ ├── CHANGELOG.md │ ├── cramkeys │ │ ├── @ashish🛠 │ │ ├── @jagan🛠 │ │ ├── @murali🛠 │ │ ├── @naresh🛠 │ │ ├── @alice🛠 │ │ ├── @bob🛠 │ │ ├── @colin🛠 │ │ ├── @gkc🛠 │ │ ├── @kevin🛠 │ │ ├── @purnima🛠 │ │ ├── @sitaram🛠 │ │ ├── @barbara🛠 │ │ ├── @emoji🦄🛠 │ │ ├── @sameeraja🛠 │ │ ├── @company🛠 │ │ ├── @egbiometric🛠 │ │ ├── @egcovidlab🛠 │ │ ├── @egcreditbureau🛠 │ │ ├── @eggovagency🛠 │ │ ├── @at_name_drop🛠 │ │ ├── @foundation🛠 │ │ └── @atsign_settings🛠 │ ├── test │ │ └── at_cram_test.dart │ ├── .gitignore │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── bin │ │ ├── at_cram.dart │ │ └── at_cram_single.dart │ └── README.md ├── at_cli │ ├── lib │ │ ├── at_cli.dart │ │ └── src │ │ │ ├── preference.dart │ │ │ ├── command_line_parser.dart │ │ │ ├── config_util.dart │ │ │ └── at_cli.dart │ ├── CHANGELOG.md │ ├── analysis_options.yaml │ ├── pubspec.yaml │ ├── LICENSE │ ├── bin │ │ └── main.dart │ └── README.md └── at_dump_atKeys │ ├── CHANGELOG.md │ ├── test │ └── at_pkam_test.dart │ ├── .gitignore │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── bin │ ├── at_pkam.dart │ ├── main.dart │ └── generate_at_demo_data.dart │ ├── lib │ └── commandline_parser.dart │ └── README.md ├── melos.yaml ├── pubspec.yaml ├── .github ├── dependabot.yml └── workflows │ ├── autobug.yaml │ ├── static_analysis.yml │ └── scorecards.yml ├── tools └── build-tools.sh ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── CONTRIBUTING.md └── code_of_conduct.md /packages/at_repl/doc/api/categories.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /packages/at_pkam/lib/at_pkam.dart: -------------------------------------------------------------------------------- 1 | int calculate() { 2 | return 6 * 7; 3 | } 4 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/lib/at_ve_doctor.dart: -------------------------------------------------------------------------------- 1 | int calculate() { 2 | return 6 * 7; 3 | } 4 | -------------------------------------------------------------------------------- /packages/at_pkam/lib/pkam_constants.dart: -------------------------------------------------------------------------------- 1 | const String ZIP = 'zip'; 2 | const String AT_KEYS = 'atKeys'; 3 | -------------------------------------------------------------------------------- /packages/at_repl/lib/version.dart: -------------------------------------------------------------------------------- 1 | // Generated code. Do not modify. 2 | const packageVersion = '2.0.0'; 3 | -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: at_tools 2 | 3 | packages: 4 | - packages/** 5 | 6 | scripts: 7 | build-tools: ./tools/build-tools.sh 8 | -------------------------------------------------------------------------------- /packages/at_repl/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | -------------------------------------------------------------------------------- /packages/at_hive_recovery/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1 2 | 3 | - Released pub global binary 4 | 5 | ## 1.0.0 6 | 7 | - Initial version -------------------------------------------------------------------------------- /packages/at_repl/example/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsign-foundation/at_tools/HEAD/packages/at_repl/example/image.png -------------------------------------------------------------------------------- /packages/at_repl/example/local.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsign-foundation/at_tools/HEAD/packages/at_repl/example/local.gif -------------------------------------------------------------------------------- /packages/at_repl/example/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsign-foundation/at_tools/HEAD/packages/at_repl/example/usage.gif -------------------------------------------------------------------------------- /packages/at_repl/lib/repl_mode.dart: -------------------------------------------------------------------------------- 1 | enum ReplMode { 2 | main, 3 | inspectKeys, 4 | inspectNotifications, 5 | monitor 6 | } -------------------------------------------------------------------------------- /packages/at_cram/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version, created by Stagehand 4 | 5 | ## 1.1.0 6 | 7 | - Null safety upgrade 8 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_tools_workspace 2 | 3 | environment: 4 | sdk: ">=2.18.0 <4.0.0" 5 | 6 | dev_dependencies: 7 | melos: ^3.0.1 8 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@ashish🛠: -------------------------------------------------------------------------------- 1 | feacb0894de2d9476e903be2164b01194dcce1490acf6d588400ef469cdd6eb1027e2baae02acd820c3a4727905f3e4866572714fe554aa2284ec8bdced0d767 -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@jagan🛠: -------------------------------------------------------------------------------- 1 | 0f0ecff314fc3183baea1e94f125e268005557b4763dc744ea41c5693161084d8127d768566613313b1dff887c87be6a80a1fc6fc09d5234fcad093cea82d855 -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@murali🛠: -------------------------------------------------------------------------------- 1 | 721741f1f9e191ac85f8af185b57627f6b2a66be8f8c40228d15a8b17373ceaf1c17ff575af621d3b231f63b06a8ba3661c551d301b79b04e8ff9063f00c2de8 -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@naresh🛠: -------------------------------------------------------------------------------- 1 | 680b1d580b4bdfb5231b6db336fa4713e29726ee5f4f9ea776d1853c8c94043a2e83b97f1d97adb6c971f0536da0574287314bea647cd5096fe8025222180712 -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atsign-foundation/at_tools/HEAD/packages/at_repl/doc/api/static-assets/favicon.png -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@alice🛠: -------------------------------------------------------------------------------- 1 | b26455a907582760ebf35bc4847de549bc41c24b25c8b1c58d5964f7b4f8a43bc55b0e9a601c9a9657d9a8b8bbc32f88b4e38ffaca03c8710ebae1b14ca9f364 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@bob🛠: -------------------------------------------------------------------------------- 1 | 33c2df30b79743ff880fc1c832a5c69170974dd736231b84ee360df89a0faff1f6efe0e83064144a7b4e5029334ad1daedc49bf82c0be1f763f590c28e33ba0a 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@colin🛠: -------------------------------------------------------------------------------- 1 | 540f1b5fa05b40a58ea7ef82d3cfcde9bb72db8baf4bc863f552f82695837b9fee631f773ab3e34dde05b51e900220e6ae6f7240ec9fc1d967252e1aea4064ba 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@gkc🛠: -------------------------------------------------------------------------------- 1 | c292e64ebe852ac39ef39ac5e83392672d66bdfe0e1d496bf777347019271b05012756b7d19d18beefb5f21027ef6e66b5f1637280982ca7bb295c94adf85f23 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@kevin🛠: -------------------------------------------------------------------------------- 1 | e0d06915c3f81561fb5f8929caae64a7231db34fdeaff939aacac3cb736be8328c2843b518a2fc7a58fcec8c0aa98c735c0ce5f8ce880e97cd61cf1f2751efc5 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@purnima🛠: -------------------------------------------------------------------------------- 1 | 91890877b8054eb456e6dbd0a739d5433c200529cbe885754b7d9947b8eaa317459785bec8afb904eec947ddb36e8a22eafe12a54a39373d381f08c468ccea3f -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@sitaram🛠: -------------------------------------------------------------------------------- 1 | 15cdce8f92bcf7e742d5b75dc51ec06d798952f8bf7e8ff4c2b6448e5f7c2c12b570fe945f04011455fdc49cacdf9393d9c1ac4609ec71c1a0b0c213578e7ec7 -------------------------------------------------------------------------------- /packages/at_cli/lib/at_cli.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library at_cli; 5 | 6 | export 'src/at_cli.dart'; 7 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@barbara🛠: -------------------------------------------------------------------------------- 1 | b43edaa255f738c763c2c79df4d49a4173b02c132e164b77650383fffe16f045ef8065c2ec05df8f4ae3475e04e83c7c99e96f52de9c4d1a915b67d24f590c99 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@emoji🦄🛠: -------------------------------------------------------------------------------- 1 | 1fcb2d6d665d3a79e2eafaec38947a4f4ca3db79430a47fb43b510f46ef9a03991a0ba4ec217ff2173b5cf1324737787263c1dd28b2c0979ea06ed79f0b40118 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@sameeraja🛠: -------------------------------------------------------------------------------- 1 | 67f75d44efbbd30547b9d090cf67134bc591b65ea7e1c4dec42d08e50ec4b89fc7ba5431ab1ae0b1fb4c99878955ca1f90798422526a070ef1bf2ad428c6353a 2 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1 2 | - work around a breaking change in `encrypt` package 5.0.2 3 | 4 | ## 1.0.0 5 | 6 | - Initial version, derived from PKAM tool -------------------------------------------------------------------------------- /packages/at_cram/test/at_cram_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | void main() { 4 | test('hello world', () { 5 | expect(true, equals(true)); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@company🛠: -------------------------------------------------------------------------------- 1 | ikpnBQu2SAQsgxNb6mLoGQrVGK4GrUB2wjWLGzHisojHUtm27-tv4knY8KgPeoUZGxXQVofFoLoltqkQbYnzJeLI6864jMxJyjEKc82DYtvQextapZUZVjliZRTaNsbS6Vd8bYqE7iEEDEHcj60hV5 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@egbiometric🛠: -------------------------------------------------------------------------------- 1 | LsAi8y2qXh6SJwl2C3LIuuZUo3SiZ8fNAH6b7FvUmimShvOu-nSMhYrFEZGeoIf2rNu148AD7QNCD5GJjHWe7FuLW4Q0xvhmw8uvONlipE3VxCVDj8AyQiS6lyn6SPTi-GnNlDTO10d-4aEMKyHXin -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@egcovidlab🛠: -------------------------------------------------------------------------------- 1 | VkBTIoOH8LdgQ3ZZTgwQcCue8r7AkYZIa4iXtkPWQzP2XxZbaok51FkenmTi7VAycS4NhtorLjHyiipenAO6kjaLJ-qyqc-C07rO0QcWIPi5b0jIMo-zyTTFnPQP1nmmSI306ltdKkWu8W6gsmCTqq -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@egcreditbureau🛠: -------------------------------------------------------------------------------- 1 | NC6gYrJMRM5R4N-FUQs7KTv1jrOlj8x8ai1JFFBBrflcUxihJHtJCArMx5eENuvHn4imKnlbrbyYuJUlsYoVTkid9gAwXco4-3rd0thxzwo9bYHNW4hYkAqISElyxbzO9s20NFbbWSEkhoGN4IY9yJ -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@eggovagency🛠: -------------------------------------------------------------------------------- 1 | ikpnBQu2SAQsgxNb6mLoGQrVGK4GrUB2wjWLGzHisojHUtm27-tv4knY8KgPeoUZGxXQVofFoLoltqkQbYnzJeLI6864jMxJyjEKc82DYtvQextapZUZVjliZRTaNsbS6Vd8bYqE7iEEDEHcj60hV5 -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@at_name_drop🛠: -------------------------------------------------------------------------------- 1 | NC6gYrJMRM5R4N-FUQs7KTv1jrOlj8x8ai1JFFBBrflcUxihJHtJCArMx5eENuvHn4imKnlbrbyYuJUlsYoVTkid9gAwXco4-3rd0thxzwo9bYHNW4hYkAqISElyxbzO9s20NFbbWSEkhoGN4IY9yJ 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@foundation🛠: -------------------------------------------------------------------------------- 1 | LsAi8y2qXh6SJwl2C3LIuuZUo3SiZ8fNAH6b7FvUmimShvOu-nSMhYrFEZGeoIf2rNu148AD7QNCD5GJjHWe7FuLW4Q0xvhmw8uvONlipE3VxCVDj8AyQiS6lyn6SPTi-GnNlDTO10d-4aEMKyHXin 2 | -------------------------------------------------------------------------------- /packages/at_cram/cramkeys/@atsign_settings🛠: -------------------------------------------------------------------------------- 1 | VkBTIoOH8LdgQ3ZZTgwQcCue8r7AkYZIa4iXtkPWQzP2XxZbaok51FkenmTi7VAycS4NhtorLjHyiipenAO6kjaLJ-qyqc-C07rO0QcWIPi5b0jIMo-zyTTFnPQP1nmmSI306ltdKkWu8W6gsmCTqq 2 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.1 2 | 3 | - Release pub global binary 4 | 5 | ## 1.0.0 6 | 7 | - Initial version, created by Stagehand 8 | 9 | ## 1.1.0 10 | 11 | - Null safety upgrade 12 | -------------------------------------------------------------------------------- /packages/at_pkam/test/at_pkam_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:at_pkam/at_pkam.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('calculate', () { 6 | expect(calculate(), 42); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /packages/at_cram/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs 6 | build/ 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/test/at_pkam_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:at_pkam/at_pkam.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('calculate', () { 6 | expect(calculate(), 42); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /packages/at_pkam/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs 6 | build/ 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs 6 | build/ 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs 6 | build/ 7 | 8 | # Directory created by dartdoc 9 | doc/api/ 10 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/test/at_ve_doctor_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:at_ve_doctor/at_ve_doctor.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('calculate', () { 6 | expect(calculate(), 42); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/index.json: -------------------------------------------------------------------------------- 1 | [{"name":"at_repl","qualifiedName":"at_repl","href":"at_repl/at_repl-library.html","type":"library","overriddenDepth":0,"packageName":"at_repl","desc":"# activate at_repl\n$ dart pub global activate at_repl\n"}] 2 | -------------------------------------------------------------------------------- /packages/at_cli/lib/src/preference.dart: -------------------------------------------------------------------------------- 1 | class AtCliPreference { 2 | String rootDomain = 'root.atsign.org'; 3 | int rootPort = 64; 4 | bool authRequired = false; 5 | late String authMode; 6 | late String authKeyFile; 7 | String namespace = ''; 8 | } 9 | -------------------------------------------------------------------------------- /packages/at_pkam/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.2 2 | - work around a breaking change in `encrypt` package 5.0.2 3 | 4 | ## 1.1.1 5 | 6 | - Released pub global binary 7 | 8 | ## 1.0.0 9 | 10 | - Initial version, created by Stagehand 11 | 12 | ## 1.1.0 13 | 14 | - Null safety upgrade 15 | -------------------------------------------------------------------------------- /packages/at_repl/example/example.md: -------------------------------------------------------------------------------- 1 | ## Activate Locally 2 | 3 | ![](https://github.com/atsign-foundation/at_tools/tree/trunk/packages/at_repl/example/local.gif) 4 | 5 | ## Usage 6 | 7 | ![](https://github.com/atsign-foundation/at_tools/tree/trunk/packages/at_repl/example/usage.gif) 8 | -------------------------------------------------------------------------------- /packages/at_cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.1.2 2 | - work around a breaking change in `encrypt` package 5.0.2 3 | ## 3.1.1 4 | - Released pub global binary 5 | ## 3.1.0 6 | - Use latest packages 7 | - Fixed lint warnings 8 | ## 1.0.0 9 | - initial version 10 | ## 1.1.0 11 | - null safety upgrade 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" 12 | -------------------------------------------------------------------------------- /packages/at_cram/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_cram 2 | description: A command-line utility to generate CRAM digest 3 | version: 1.1.0 4 | 5 | environment: 6 | sdk: '>=2.12.0 <4.0.0' 7 | 8 | #dependencies: 9 | # path: ^1.7.0 10 | 11 | dev_dependencies: 12 | lints: ^1.0.1 13 | test: ^1.17.5 14 | 15 | dependencies: 16 | args: ^2.3.1 17 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/play_button.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/readme.md: -------------------------------------------------------------------------------- 1 | # highlight.js 2 | 3 | Generated from https://highlightjs.org/download/ on 2021-07-13 4 | 5 | **Included languages:** 6 | 7 | * bash 8 | * c 9 | * css 10 | * dart 11 | * diff 12 | * html, xml 13 | * java 14 | * javascript 15 | * json 16 | * kotlin 17 | * markdown 18 | * objective-c 19 | * plaintext 20 | * shell 21 | * swift 22 | * yaml 23 | -------------------------------------------------------------------------------- /packages/at_repl/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.0.0 2 | 3 | - feat: `/inspect` command 4 | - feat: `/monitor [regex]` command 5 | - feat `/inspect_notify` command 6 | 7 | ## 1.0.2 8 | 9 | - feat: added tests 10 | - feat: more verbose exceptions 11 | 12 | ## 1.0.1 13 | 14 | - fix: improper formatting of shared keys 15 | - fix: modified namespace formatting 16 | 17 | ## 1.0.0 18 | 19 | - feat: initial version. 20 | -------------------------------------------------------------------------------- /packages/at_repl/lib/repl_exception.dart: -------------------------------------------------------------------------------- 1 | class REPLException implements Exception { 2 | final String message; 3 | final Exception? cause; 4 | 5 | const REPLException(this.message, [this.cause]); 6 | 7 | @override 8 | String toString() { 9 | if (cause != null) { 10 | return 'REPLException: $message\nCaused by: $cause'; 11 | } 12 | return 'REPLException: $message'; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/at_hive_recovery/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_hive_recovery 2 | description: A command-line utility to recover corrupt hive box 3 | version: 1.0.1 4 | publish_to: none 5 | 6 | executables: 7 | at_hive_recovery: main 8 | 9 | environment: 10 | sdk: '>=2.12.0 <4.0.0' 11 | 12 | dependencies: 13 | at_persistence_secondary_server: ^3.0.5 14 | crypto: ^3.0.1 15 | 16 | dev_dependencies: 17 | lints: ^1.0.1 18 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_ve_doctor 2 | description: A sample command-line application. 3 | publish_to: none 4 | version: 1.1.1 5 | # homepage: https://www.example.com 6 | 7 | executables: 8 | at_ve_doctor: at_ve_doctor 9 | 10 | environment: 11 | sdk: '>=2.12.0 <4.0.0' 12 | 13 | dependencies: 14 | at_server_status: ^1.0.3 15 | 16 | dev_dependencies: 17 | lints: ^1.0.1 18 | test: ^1.14.4 19 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_dump_atKeys 2 | description: A command-line utility to dump keys from an atKeys file 3 | version: 1.0.1 4 | 5 | environment: 6 | sdk: '>=2.12.0 <4.0.0' 7 | 8 | dependency_overrides: 9 | at_pkam: 10 | path: ../at_pkam 11 | dependencies: 12 | crypton: ^2.0.1 13 | encrypt: ^5.0.0 14 | archive: ^3.1.2 15 | 16 | dev_dependencies: 17 | lints: ^1.0.1 18 | test: ^1.17.5 19 | -------------------------------------------------------------------------------- /packages/at_pkam/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_pkam 2 | description: A command-line utility to generate PKAM digest 3 | version: 1.1.2 4 | repository: https://github.com/atsign-foundation/at_tools 5 | 6 | executables: 7 | at_pkam: main 8 | 9 | environment: 10 | sdk: '>=2.12.0 <4.0.0' 11 | 12 | dependencies: 13 | args: ^2.4.1 14 | archive: ^3.1.2 15 | crypton: ^2.0.1 16 | encrypt: ^5.0.0 17 | path: ^1.8.3 18 | 19 | dev_dependencies: 20 | lints: ^1.0.1 21 | test: ^1.17.5 22 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/at_cli/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:lints/recommended.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/at_cram/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:lints/recommended.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/at_pkam/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:lints/recommended.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:lints/recommended.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:lints/recommended.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/at_repl/lib/constants.dart: -------------------------------------------------------------------------------- 1 | /// Default regex patterns used throughout the at_repl application 2 | 3 | /// Default regex for inspect command that excludes system keys 4 | /// Excludes: shared_key, signing_privatekey, signing_publickey, publickey 5 | const String defaultInspectRegex = '^(?!.*(shared_key|signing_privatekey|signing_publickey|publickey)).*'; 6 | 7 | /// Default regex for monitor command that excludes statsNotification 8 | /// Excludes: statsNotification 9 | const String defaultMonitorRegex = '^(?!.*statsNotification).*'; -------------------------------------------------------------------------------- /packages/at_repl/lib/interactive_session.dart: -------------------------------------------------------------------------------- 1 | /// Base interface for interactive sessions in the REPL 2 | abstract class InteractiveSession { 3 | /// Handle input from the user during the interactive session 4 | /// Returns true if input was handled, false if session should exit 5 | bool handleInput(String input); 6 | 7 | /// Get the current prompt to display to the user 8 | String getPrompt(); 9 | 10 | /// Check if the session is still active 11 | bool get isActive; 12 | 13 | /// Exit the session and clean up resources 14 | void exit(); 15 | } 16 | -------------------------------------------------------------------------------- /tools/build-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir="$(dirname -- "$(readlink -f -- "$0")")" 4 | cd "$script_dir/.." || exit 1 # cd to root of repo 5 | 6 | melos bootstrap --scope=at_cli --scope=at_cram --scope=at_pkam --scope=at_repl 7 | 8 | mkdir -p build-tools 9 | 10 | dart compile exe packages/at_cli/bin/main.dart -o build-tools/at_cli 11 | dart compile exe packages/at_cram/bin/at_cram.dart -o build-tools/at_cram 12 | dart compile exe packages/at_pkam/bin/main.dart -o build-tools/at_pkam 13 | dart compile exe packages/at_repl/bin/at_repl.dart -o build-tools/at_repl 14 | -------------------------------------------------------------------------------- /packages/at_cli/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_cli 2 | description: A command line utility for at protocol commands. 3 | version: 3.1.2 4 | repository: https://github.com/atsign-foundation/at_tools 5 | homepage: https://atsign.dev 6 | 7 | executables: 8 | at_cli: main 9 | 10 | environment: 11 | sdk: '>=2.12.0 <4.0.0' 12 | 13 | dependencies: 14 | args: ^2.4.0 15 | at_lookup: ^3.0.35 16 | at_client: ^3.0.56 17 | at_commons: ^3.0.45 18 | at_utils: ^3.0.12 19 | crypton: ^2.1.0 20 | encrypt: ^5.0.1 21 | yaml: ^3.1.1 22 | yaml_writer: ^1.0.3 23 | 24 | dev_dependencies: 25 | lints: ^1.0.1 26 | test: ^1.17.0 27 | test_process: ^2.0.0 28 | -------------------------------------------------------------------------------- /packages/at_repl/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: at_repl 2 | description: A command line tool that interacts with the at_platform. Uses some custom functions as well as protocol verbs. 3 | version: 2.0.0+1 4 | repository: https://github.com/atsign-foundation/at_tools 5 | environment: 6 | sdk: ^3.0.0 7 | 8 | executables: 9 | at_repl: at_repl 10 | 11 | dependencies: 12 | at_client: ^3.0.61 13 | at_commons: ^5.0.2 14 | at_onboarding_cli: ^1.11.0 15 | at_utils: ^3.0.12 16 | build_version: ^2.1.1 17 | build_runner: ^2.4.5 18 | pub_updater: ^0.3.0 19 | at_cli_commons: ^1.2.1 20 | io: ^1.0.5 21 | args: ^2.6.0 22 | # path: ^1.8.0 23 | 24 | dev_dependencies: 25 | lints: ^2.0.0 26 | test: ^1.21.0 27 | -------------------------------------------------------------------------------- /packages/at_cram/bin/at_cram.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:crypto/crypto.dart'; 4 | import 'package:path/path.dart'; 5 | 6 | void main(List arguments) { 7 | final secret = getSecret(arguments[0]).trim(); 8 | var challenge = stdin.readLineSync()!; 9 | challenge = challenge.trim(); 10 | var combo = '$secret$challenge'; 11 | var bytes = utf8.encode(combo); 12 | var digest = sha512.convert(bytes); 13 | stdout.write(digest); 14 | stdout.write('\n'); 15 | } 16 | 17 | String getSecret(String filename) { 18 | var pathToFile = join(dirname(Platform.script.toFilePath()), filename); 19 | var contents = File(pathToFile).readAsStringSync(); 20 | return (contents); 21 | } 22 | -------------------------------------------------------------------------------- /packages/at_hive_recovery/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | # at_hive_recovery 4 | 5 | **at_hive_recovery** tool for recovering corrupted hive boxes. 6 | 7 | Usage: 8 | 9 | ``` 10 | dart bin/main.dart 11 | ``` 12 | 13 | e.g 14 | 15 | ``` 16 | dart bin/main.dart @alice /home/alice/storage/hive 17 | ``` -------------------------------------------------------------------------------- /packages/at_hive_recovery/bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:at_hive_recovery/src/hive_doctor.dart'; 4 | 5 | void main(var args) async { 6 | if (args == null || args.length < 2) { 7 | print('usage: dart bin/main dart '); 8 | exit(0); 9 | } 10 | final atSign = args[0]; 11 | final hiveStorageDir = args[1]; 12 | final hiveDoctor = HiveDoctor(); 13 | hiveDoctor.init(hiveStorageDir); 14 | final recoveryResult = await hiveDoctor.recoverBox(atSign); 15 | print('hive recovery result: $recoveryResult'); 16 | if (recoveryResult) { 17 | print('hive box successfully recovered'); 18 | } else { 19 | print('unable to recover hive box'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/autobug.yaml: -------------------------------------------------------------------------------- 1 | name: Auto Assign Issues to Triage column in Sprint Planning Project Board 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | permissions: # added using https://github.com/step-security/secure-workflows 8 | contents: read 9 | 10 | jobs: 11 | issue_opened: 12 | name: issue_opened 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: 'Move issue to "Triage"' 16 | uses: leonsteinhaeuser/project-beta-automations@939000fb1900c9fc4f7b5058a09d9f833ebc6859 # v2.2.1 17 | with: 18 | gh_token: ${{ secrets.MY_GITHUB_TOKEN }} 19 | organization: atsign-foundation 20 | project_id: 8 21 | resource_node_id: ${{ github.event.issue.node_id }} 22 | status_value: "Triage" 23 | -------------------------------------------------------------------------------- /packages/at_pkam/bin/at_pkam.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | import 'package:path/path.dart'; 6 | 7 | void main(List arguments) { 8 | final privateKey = getSecret(arguments[0]).trim(); 9 | var key = RSAPrivateKey.fromString(privateKey); 10 | var challenge = stdin.readLineSync()!; 11 | challenge = challenge.trim(); 12 | var signature = 13 | base64.encode(key.createSHA256Signature(utf8.encode(challenge))); 14 | stdout.write(signature); 15 | stdout.write('\n'); 16 | } 17 | 18 | String getSecret(String filename) { 19 | var pathToFile = join(dirname(Platform.script.toFilePath()), filename); 20 | var contents = File(pathToFile).readAsStringSync(); 21 | return (contents); 22 | } 23 | -------------------------------------------------------------------------------- /packages/at_hive_recovery/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # Avoid committing generated Javascript files: 15 | *.dart.js 16 | *.info.json # Produced by the --dump-info flag. 17 | *.js # When generated by dart2js. Don't specify *.js if your 18 | # project includes source files written in JavaScript. 19 | *.js_ 20 | *.js.deps 21 | *.js.map 22 | 23 | # IntelliJ related 24 | *.iml 25 | *.ipr 26 | *.iws 27 | *.idea/ 28 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/bin/at_pkam.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:crypton/crypton.dart'; 5 | import 'package:path/path.dart'; 6 | 7 | void main(List arguments) { 8 | var name = arguments[0]; 9 | var privateKey = getSecret(name); 10 | privateKey = privateKey.trim(); 11 | var key = RSAPrivateKey.fromString(privateKey); 12 | var challenge = stdin.readLineSync()!; 13 | challenge = challenge.trim(); 14 | var signature = 15 | base64.encode(key.createSHA256Signature(utf8.encode(challenge))); 16 | stdout.write(signature); 17 | stdout.write('\n'); 18 | } 19 | 20 | String getSecret(String filename) { 21 | var pathToFile = join(dirname(Platform.script.toFilePath()), filename); 22 | var contents = File(pathToFile).readAsStringSync(); 23 | return (contents); 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | pubspec_overrides.yaml 10 | 11 | build-tools/ 12 | 13 | # Directory created by dartdoc 14 | # If you don't generate documentation locally you can remove this line. 15 | doc/api/ 16 | 17 | # Avoid committing generated Javascript files: 18 | *.dart.js 19 | *.info.json # Produced by the --dump-info flag. 20 | *.js # When generated by dart2js. Don't specify *.js if your 21 | # project includes source files written in JavaScript. 22 | *.js_ 23 | *.js.deps 24 | *.js.map 25 | 26 | # IntelliJ related 27 | *.iml 28 | *.ipr 29 | *.iws 30 | *.idea/ 31 | 32 | #vscode 33 | .vscode 34 | 35 | pubspec_overrides.yaml #melos 36 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/delete.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:at_client/at_client.dart'; 3 | import 'package:io/ansi.dart'; 4 | 5 | Future delete(AtClient atClient, {required String atKeyStr}) async { 6 | AtKey atKey = AtKey.fromString(atKeyStr); 7 | return await atClient.delete(atKey, 8 | deleteRequestOptions: DeleteRequestOptions()..useRemoteAtServer = true); 9 | } 10 | 11 | void handleDelete(String input, AtClient atClient, IOSink outputStream) async { 12 | final parts = input.split(' '); 13 | if (parts.length < 2) { 14 | outputStream.writeln(red.wrap("Usage: /delete ")); 15 | return; 16 | } 17 | final atKeyStr = parts.sublist(1).join(' '); 18 | 19 | try { 20 | final success = await delete(atClient, atKeyStr: atKeyStr); 21 | if (success) { 22 | outputStream.writeln(green.wrap("Successfully deleted: $atKeyStr")); 23 | } else { 24 | outputStream.writeln(red.wrap("Failed to delete key: $atKeyStr")); 25 | } 26 | } catch (e) { 27 | outputStream.writeln(red.wrap("Error deleting key: $e")); 28 | } 29 | } -------------------------------------------------------------------------------- /packages/at_ve_doctor/bin/at_ve_doctor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:at_server_status/at_server_status.dart'; 4 | 5 | void main() async { 6 | var atSigns = [ 7 | '@alice🛠', 8 | '@ashish🛠', 9 | '@barbara🛠', 10 | '@bob🛠', 11 | '@colin🛠', 12 | '@egbiometric🛠', 13 | '@egcovidlab🛠', 14 | '@egcreditbureau🛠', 15 | '@eggovagency🛠', 16 | '@emoji🦄🛠', 17 | '@eve🛠', 18 | '@jagan🛠', 19 | '@kevin🛠', 20 | '@murali🛠', 21 | '@naresh🛠', 22 | '@purnima🛠', 23 | '@sameeraja🛠', 24 | '@sitaram🛠' 25 | ]; 26 | 27 | Future getAtStatus(atSign) async { 28 | AtStatus atStatus; 29 | AtStatusImpl atStatusImpl; 30 | atStatusImpl = AtStatusImpl(); 31 | 32 | atStatusImpl.rootUrl = 'vip.ve.atsign.zone'; 33 | atStatus = await atStatusImpl.get(atSign); 34 | print('$atSign status: ${atStatus.status()}'); 35 | return atStatus; 36 | } 37 | 38 | await Future.forEach(atSigns, (dynamic element) async { 39 | await getAtStatus(element); 40 | }); 41 | 42 | exit(0); 43 | } 44 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/get.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:at_client/at_client.dart'; 3 | import 'package:io/ansi.dart'; 4 | 5 | Future get(AtClient atClient, {required String atKeyStr}) async { 6 | AtKey atKey = AtKey.fromString(atKeyStr); 7 | AtValue? atValue = await atClient.get(atKey, 8 | getRequestOptions: GetRequestOptions()..useRemoteAtServer = true); 9 | return atValue.value; 10 | } 11 | 12 | void handleGet(String input, AtClient atClient, IOSink outputStream) async { 13 | final parts = input.split(' '); 14 | if (parts.length < 2) { 15 | outputStream.writeln(red.wrap("Usage: /get ")); 16 | return; 17 | } 18 | final atKeyStr = parts.sublist(1).join(' '); 19 | 20 | try { 21 | final value = await get(atClient, atKeyStr: atKeyStr); 22 | if (value != null) { 23 | outputStream.writeln(green.wrap("Value: $value")); 24 | } else { 25 | outputStream.writeln(lightYellow.wrap("Key not found or has no value")); 26 | } 27 | } catch (e) { 28 | outputStream.writeln(red.wrap("Error getting key: $e")); 29 | } 30 | } -------------------------------------------------------------------------------- /packages/at_repl/lib/features/put.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:at_client/at_client.dart'; 3 | import 'package:io/ansi.dart'; 4 | 5 | Future put(AtClient atClient, {required String atKeyStr, required String value}) async { 6 | return atClient.put(AtKey.fromString(atKeyStr), value, 7 | putRequestOptions: PutRequestOptions()..useRemoteAtServer = true); 8 | } 9 | 10 | void handlePut(String input, AtClient atClient, IOSink outputStream) async { 11 | final parts = input.split(' '); 12 | if (parts.length < 3) { 13 | outputStream.writeln(red.wrap("Usage: /put ")); 14 | return; 15 | } 16 | final atKeyStr = parts[1]; 17 | final value = parts.sublist(2).join(' '); 18 | 19 | try { 20 | final success = await put(atClient, atKeyStr: atKeyStr, value: value); 21 | if (success) { 22 | outputStream.writeln(green.wrap("Successfully stored: $atKeyStr = $value")); 23 | } else { 24 | outputStream.writeln(red.wrap("Failed to store key: $atKeyStr")); 25 | } 26 | } catch (e) { 27 | outputStream.writeln(red.wrap("Error storing key: $e")); 28 | } 29 | } -------------------------------------------------------------------------------- /packages/at_dump_atKeys/lib/commandline_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:args/args.dart'; 2 | 3 | /// A class for taking a list of raw command line arguments and parsing out 4 | /// options and flags from them. 5 | class CommandLineParser { 6 | /// Parses [arguments], a list of command-line arguments, matches them against the 7 | /// flags and options defined by this parser, and returns the result. 8 | ArgResults getParserResults(List arguments, ArgParser parser) { 9 | var results; 10 | try { 11 | if (arguments.isNotEmpty) { 12 | results = parser.parse(arguments); 13 | if (results.options.length != parser.options.length) { 14 | throw ArgParserException('Invalid Arguments \n' + parser.usage); 15 | } 16 | } else { 17 | throw ArgParserException('ArgParser Exception \n' + parser.usage); 18 | } 19 | return results; 20 | } on ArgParserException { 21 | throw ArgParserException('ArgParserException\n' + parser.usage); 22 | } 23 | } 24 | 25 | ArgParser getParser() { 26 | var parser = ArgParser(); 27 | parser.addOption('file_path', 28 | abbr: 'p', help: '.atKeys file path which contains keys'); 29 | return parser; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/at_repl/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | linter: 17 | rules: 18 | camel_case_types : true 19 | unnecessary_string_interpolations : true 20 | await_only_futures : true 21 | unawaited_futures: true 22 | depend_on_referenced_packages : false 23 | 24 | # analyzer: 25 | # exclude: 26 | # - path/to/excluded/files/** 27 | 28 | # For more information about the core and recommended set of lints, see 29 | # https://dart.dev/go/core-lints 30 | 31 | # For additional information about configuring this file, see 32 | # https://dart.dev/guides/language/analysis-options 33 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/scan.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:at_client/at_client.dart'; 3 | import 'package:io/ansi.dart'; 4 | 5 | Future> getAtKeys(AtClient atClient, {String? regex, bool showHiddenKeys = true}) async { 6 | return await atClient.getAtKeys(regex: regex, showHiddenKeys: showHiddenKeys); 7 | } 8 | 9 | void handleScan(String input, AtClient atClient, IOSink outputStream) async { 10 | final parts = input.split(' '); 11 | final regex = parts.length > 1 ? parts.sublist(1).join(' ') : null; 12 | 13 | try { 14 | outputStream.writeln(cyan.wrap("Scanning for keys${regex != null ? ' with regex: $regex' : ''}...")); 15 | final keys = await getAtKeys(atClient, regex: regex); 16 | 17 | if (keys.isEmpty) { 18 | outputStream.writeln(lightYellow.wrap("No keys found")); 19 | return; 20 | } 21 | 22 | outputStream.writeln(green.wrap("\nFound ${keys.length} key(s):")); 23 | outputStream.writeln("${'#'.padRight(5)} | Key"); 24 | outputStream.writeln("${'─' * 5}─┼─${'─' * 50}"); 25 | 26 | for (int i = 0; i < keys.length; i++) { 27 | final indexStr = (i + 1).toString().padRight(5); 28 | outputStream.writeln("$indexStr | ${keys[i].toString()}"); 29 | } 30 | 31 | } catch (e) { 32 | outputStream.writeln(red.wrap("Error scanning keys: $e")); 33 | } 34 | } -------------------------------------------------------------------------------- /packages/at_pkam/lib/commandline_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:args/args.dart'; 2 | 3 | /// A class for taking a list of raw command line arguments and parsing out 4 | /// options and flags from them. 5 | class CommandLineParser { 6 | /// Parses [arguments], a list of command-line arguments, matches them against the 7 | /// flags and options defined by this parser, and returns the result. 8 | ArgResults getParserResults(List arguments, ArgParser parser) { 9 | var results; 10 | try { 11 | if (arguments.isNotEmpty) { 12 | results = parser.parse(arguments); 13 | if (results.options.length != parser.options.length) { 14 | throw ArgParserException('Invalid Arguments \n' + parser.usage); 15 | } 16 | } else { 17 | throw ArgParserException('ArgParser Exception \n' + parser.usage); 18 | } 19 | return results; 20 | } on ArgParserException { 21 | throw ArgParserException('ArgParserException\n' + parser.usage); 22 | } 23 | } 24 | 25 | ArgParser getParser() { 26 | var parser = ArgParser(); 27 | parser.addOption('file_path', 28 | abbr: 'p', help: '.keys or .zip file path which contains keys'); 29 | parser.addOption('from_response', abbr: 'r', help: 'from:@atSign response'); 30 | parser.addOption('aes_key', 31 | abbr: 'a', help: 'aes key file path', defaultsTo: ''); 32 | return parser; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/at_hive_recovery/lib/src/hive_doctor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:at_persistence_secondary_server/at_persistence_secondary_server.dart'; 6 | import 'package:hive/hive.dart'; 7 | import 'package:crypto/crypto.dart'; 8 | 9 | class HiveDoctor { 10 | late final _storagePath; 11 | 12 | void init(String storagePath) { 13 | Hive.init(storagePath); 14 | _storagePath = storagePath; 15 | if (!Hive.isAdapterRegistered(AtDataAdapter().typeId)) { 16 | Hive.registerAdapter(AtDataAdapter()); 17 | } 18 | if (!Hive.isAdapterRegistered(AtMetaDataAdapter().typeId)) { 19 | Hive.registerAdapter(AtMetaDataAdapter()); 20 | } 21 | if (!Hive.isAdapterRegistered(CommitEntryAdapter().typeId)) { 22 | Hive.registerAdapter(CommitEntryAdapter()); 23 | } 24 | if (!Hive.isAdapterRegistered(CommitOpAdapter().typeId)) { 25 | Hive.registerAdapter(CommitOpAdapter()); 26 | } 27 | } 28 | 29 | Future recoverBox(String atSign) async { 30 | var boxName = _getShaForAtSign(atSign); 31 | final secretString = File('$_storagePath/$boxName.hash').readAsStringSync(); 32 | final secret = Uint8List.fromList(secretString.codeUnits); 33 | final box = 34 | await Hive.openBox('$boxName', encryptionCipher: HiveAesCipher(secret)); 35 | return box.isOpen; 36 | } 37 | 38 | static String _getShaForAtSign(String atsign) { 39 | // encode the given atsign 40 | var bytes = utf8.encode(atsign); 41 | return sha256.convert(bytes).toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, The Atsign Foundation 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /packages/at_cli/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, The @ Company 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /packages/at_pkam/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, The Atsign Foundation 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /packages/at_repl/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, The Atsign Foundation 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.github/workflows/static_analysis.yml: -------------------------------------------------------------------------------- 1 | name: static_analysis 2 | # Runs the workflow on the below events: 3 | # 1. on pull request raised to trunk branch. 4 | # 2. on push event to trunk branch. 5 | on: 6 | push: 7 | branches: 8 | - trunk 9 | pull_request: 10 | branches: 11 | - trunk 12 | 13 | permissions: # added using https://github.com/step-security/secure-workflows 14 | contents: read 15 | 16 | jobs: 17 | static_analysis: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | package: 23 | - at_cli 24 | - at_cram 25 | - at_dump_atKeys 26 | - at_hive_recovery 27 | - at_pkam 28 | - at_repl 29 | - at_ve_doctor 30 | 31 | steps: 32 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 33 | - uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c # v1.7.1 34 | with: 35 | sdk: stable 36 | 37 | - name: Install dependencies and analyze in ${{ matrix.package }} 38 | working-directory: packages/${{ matrix.package }} 39 | run: | 40 | dart pub get 41 | dart analyze 42 | 43 | # Runs osv-scanner to find any vulnerable Dart dependencies 44 | # It needs to look at pubspec.lock files, which is why it's 45 | # placed here, as the `dart pub get` above will create them 46 | - name: Run osv-scanner 47 | uses: google/osv-scanner-action/osv-scanner-action@375a0e8ebdc98e99b02ac4338a724f5750f21213 # v2.3.1 48 | with: 49 | scan-args: |- 50 | --lockfile=./packages/${{ matrix.package }}/pubspec.lock 51 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #998; 18 | font-style: italic; 19 | } 20 | 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-subst { 24 | color: #333; 25 | font-weight: bold; 26 | } 27 | 28 | .hljs-number, 29 | .hljs-literal, 30 | .hljs-variable, 31 | .hljs-template-variable, 32 | .hljs-tag .hljs-attr { 33 | color: #008080; 34 | } 35 | 36 | .hljs-string, 37 | .hljs-doctag { 38 | color: #d14; 39 | } 40 | 41 | .hljs-title, 42 | .hljs-section, 43 | .hljs-selector-id { 44 | color: #900; 45 | font-weight: bold; 46 | } 47 | 48 | .hljs-subst { 49 | font-weight: normal; 50 | } 51 | 52 | .hljs-type, 53 | .hljs-class .hljs-title { 54 | color: #458; 55 | font-weight: bold; 56 | } 57 | 58 | .hljs-tag, 59 | .hljs-name, 60 | .hljs-attribute { 61 | color: #000080; 62 | font-weight: normal; 63 | } 64 | 65 | .hljs-regexp, 66 | .hljs-link { 67 | color: #009926; 68 | } 69 | 70 | .hljs-symbol, 71 | .hljs-bullet { 72 | color: #990073; 73 | } 74 | 75 | .hljs-built_in, 76 | .hljs-builtin-name { 77 | color: #0086b3; 78 | } 79 | 80 | .hljs-meta { 81 | color: #999; 82 | font-weight: bold; 83 | } 84 | 85 | .hljs-deletion { 86 | background: #fdd; 87 | } 88 | 89 | .hljs-addition { 90 | background: #dfd; 91 | } 92 | 93 | .hljs-emphasis { 94 | font-style: italic; 95 | } 96 | 97 | .hljs-strong { 98 | font-weight: bold; 99 | } 100 | -------------------------------------------------------------------------------- /packages/at_repl/README.md: -------------------------------------------------------------------------------- 1 | ## at_repl 2 | 3 | The Atsign FoundationThe Atsign Foundation 4 | 5 | [![pub package](https://img.shields.io/pub/v/at_repl)](https://pub.dev/packages/at_repl) 6 | [![pub points](https://img.shields.io/badge/dynamic/json?url=https://pub.dev/api/packages/at_repl/score&label=pub%20score&query=grantedPoints)](https://pub.dev/packages/at_repl/score) 7 | [![gitHub license](https://img.shields.io/badge/license-BSD3-blue.svg)](./LICENSE) 8 | 9 | A CLI tool for interacting with an atServer using the atProtocol. 10 | 11 | ### Getting Started 12 | 13 | Ensure you have your atSign keys. Keys are usually located in `$HOME/.atsign/keys`. 14 | 15 | If you don't have an atSign, visit here . 16 | 17 | If the CLI application is available on [pub](https://pub.dev), activate globally via: 18 | 19 | ```sh 20 | dart pub global activate at_repl 21 | ``` 22 | 23 | Or locally via: 24 | 25 | ```sh 26 | cd packages/at_repl 27 | dart pub global activate . -s path 28 | ``` 29 | 30 | ### Usage 31 | 32 | ```sh 33 | ➜ dart run bin/at_repl.dart --help 34 | Usage: at_repl [options] 35 | 36 | Options: 37 | -a, --atSign The atSign to use 38 | --rootUrl The root URL to connect to 39 | (defaults to "root.atsign.org:64") 40 | -k, --keys-file Path to the atKeys file 41 | -v, --[no-]verbose Enable verbose output 42 | -h, --[no-]help Show this help message 43 | ``` 44 | 45 | ### Example Execution 46 | 47 | ![screenshot of usage](./example/image.png) 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | [![GitHub License](https://img.shields.io/badge/license-BSD3-blue.svg)](./LICENSE) 4 | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/atsign-foundation/at_tools/badge)](https://securityscorecards.dev/viewer/?uri=github.com/atsign-foundation/at_tools&sort_by=check-score&sort_direction=desc) 5 | [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8121/badge)](https://www.bestpractices.dev/projects/8121) 6 | 7 | ## at_tools 8 | 9 | This repository contains atProtocol tools for developers 10 | building atPlatform applications who wish to utilize the authentication 11 | methods: 12 | 13 | ### standalone tools 14 | 15 | [at_cli](./packages/at_cli) A command line tool to execute verbs on the atPlatform. 16 | 17 | [at_cram](./packages/at_cram) The challenge–response authentication mechanism of the 18 | atProtocol. 19 | 20 | [at_dump_atKeys](./packages/at_dump_atKeys) A command line tool to dump keys from a 21 | .atKeys file. 22 | 23 | [at_pkam](./packages/at_pkam) The public key authentication mechanism of the 24 | atProtocol. 25 | 26 | [at_ve_doctor](./packages/at_ve_doctor) A very simple way to test the status of the 27 | secondaries running in the Virtual Environment. Using the 28 | [at_server_status](https://pub.dev/packages/at_server_status) package. 29 | 30 | ### Quickly build tools 31 | 32 | Using melos, we can quickly build at_pkam, at_cram, at_cli, and at_repl via the 33 | following commands: 34 | 35 | ```bash 36 | dart pub get 37 | dart run melos run build-tools 38 | ``` 39 | 40 | Then move the tools to a folder which you've exposed to the path for 41 | convenience, for example: 42 | 43 | ```bash 44 | cp ./build-tools/* ~/.local/bin/ 45 | # or 46 | sudo cp ./build-tools/* /usr/local/bin/ 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /packages/at_cram/bin/at_cram_single.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:args/args.dart'; 4 | import 'package:crypto/crypto.dart'; 5 | 6 | /// command-line usage 7 | 8 | /// (1) 9 | /// first compile this file (replace with the name of the file) 10 | /// dart compile exe bin/at_cram_single.dart -o 11 | 12 | /// (2) 13 | /// run the compiled file, comes from initial retrieval of your atSign (in your QR code or web API), comes from the `from:@sign` challenge. 14 | /// ./cram -k -c 15 | 16 | /// (3) 17 | /// program spits out digest 18 | /// in openssl, you can now do `cram:` 19 | 20 | void main(List arguments) { 21 | if (arguments.length != 4) { 22 | _printInstructions(); 23 | return; 24 | } 25 | var parser = ArgParser(); 26 | parser.addOption('cramSecret', abbr: 'k', mandatory: true, help: 'CRAM secret initially retrieved upon receiving an atSign.'); 27 | parser.addOption('challenge', 28 | abbr: 'c', mandatory: true, help: 'The challenge received after doing `from:@myatsign`, you get: `data:` where is the challenge.'); 29 | var results = parser.parse(arguments); 30 | var cramSecret = results['cramSecret']; 31 | var challenge = results['challenge']; 32 | cramSecret = cramSecret.trim(); 33 | challenge = challenge.trim(); 34 | stdout.write('\ncramSecret: $cramSecret\n'); 35 | stdout.write('challenge: $challenge\n'); 36 | var combo = '$cramSecret$challenge'; 37 | var bytes = utf8.encode(combo); 38 | var digest = sha512.convert(bytes); 39 | stdout.write('\n'); 40 | stdout.write('digest: $digest'); 41 | stdout.write('\n'); 42 | } 43 | 44 | void _printInstructions() { 45 | stdout.write('\nCRAM digesting in one single line\n'); 46 | stdout.write(' --cramSecret -k \t| cramSecret received when you initially get an @ sign (usually in the form of a QR code)\n'); 47 | stdout.write(' --challenge -c \t| the challenge response you receive after doing `from:@youratsign`, will respond with `data:`\n'); 48 | stdout.write(' Example: `dart bin/at_cram_single.dart -k cramSecret123 -c fromChallenge123`\n'); 49 | } 50 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | # Atsign Foundation Open Source Security Policies and Procedures 4 | 5 | This document outlines security procedures and general policies for the 6 | Atsign Foundation Open Source projects as found on 7 | https://github.com/atsign-foundation. 8 | 9 | * [Reporting a Vulnerability](#reporting-a-vulnerability) 10 | * [Disclosure Policy](#disclosure-policy) 11 | 12 | ## Reporting a Vulnerability 13 | 14 | The Atsign Foundation team and community take all security vulnerabilities 15 | seriously. Thank you for improving the security of our open source 16 | software. We appreciate your efforts and responsible disclosure and will 17 | make every effort to acknowledge your contributions. 18 | 19 | Report security vulnerabilities by emailing the Atsign security team at: 20 | 21 | security@atsign.com 22 | 23 | The lead maintainer will acknowledge your email within 24 hours, and will 24 | send a more detailed response within 48 hours indicating the next steps in 25 | handling your report. After the initial reply to your report, the security 26 | team will endeavor to keep you informed of the progress towards a fix and 27 | full announcement, and may ask for additional information or guidance. 28 | 29 | Please report security vulnerabilities in third-party modules to the person 30 | or team maintaining the module. 31 | 32 | ## Disclosure Policy 33 | 34 | When the security team receives a security bug report, they will assign it 35 | to a primary handler. This person will coordinate the fix and release 36 | process, involving the following steps: 37 | 38 | * Confirm the problem and determine the affected versions. 39 | * Audit code to find any potential similar problems. 40 | * Prepare fixes for all releases still under maintenance. These fixes 41 | will be released as fast as possible to pub.dev where applicable. -------------------------------------------------------------------------------- /packages/at_cli/lib/src/command_line_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:args/args.dart'; 2 | 3 | /// A class for taking a list of raw command line arguments and parsing out 4 | /// options and flags from them. 5 | class CommandLineParser { 6 | /// Parses [arguments], a list of command-line arguments, matches them against the 7 | /// flags and options defined by this parser, and returns the result. 8 | static var parser = ArgParser(); 9 | static ArgResults? getParserResults(List arguments) { 10 | ArgResults? results; 11 | // var parser = ArgParser(); 12 | parser.addFlag('auth', 13 | help: 'Set this flag if command needs auth to server', 14 | abbr: 'a', 15 | defaultsTo: false); 16 | // parser.addOption('auth', 17 | // abbr: 'a', help: 'Set this flag if command needs auth to server'); 18 | parser.addOption('mode', 19 | allowed: ['cram', 'pkam'], 20 | abbr: 'm', 21 | help: 'Choose Authentication mode if auth required', 22 | defaultsTo: 'pkam'); 23 | parser.addOption('authKeyFile', abbr: 'f', help: 'cram/pkam file path'); 24 | parser.addOption('atsign', help: 'Current Atsign'); 25 | parser.addOption('command', abbr: 'c', help: 'The at command to execute'); 26 | parser.addOption('verb', abbr: 'v', help: 'The verb to execute'); 27 | // parser.addOption('public', 28 | // abbr: 'p', help: 'set to true if key has public access'); 29 | parser.addFlag('public', 30 | abbr: 'p', 31 | help: 'set to true if key has public access', 32 | defaultsTo: false); 33 | parser.addOption('key', abbr: 'k', help: 'key to update'); 34 | parser.addOption('value', help: 'value of the key'); 35 | parser.addOption('shared_with', 36 | abbr: 'w', help: 'atsign to whom key is shared'); 37 | parser.addOption('shared_by', abbr: 'b', help: 'atsign who shared the key'); 38 | parser.addOption('regex', abbr: 'r', help: 'regex for scan'); 39 | 40 | try { 41 | if (arguments.isNotEmpty) { 42 | results = parser.parse(arguments); 43 | } 44 | return results; 45 | } on ArgParserException { 46 | throw ArgParserException('ArgParserException\n' + parser.usage); 47 | } 48 | } 49 | 50 | static String getUsage() { 51 | return parser.usage; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/at_repl/bin/at_repl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:at_repl/repl.dart'; 3 | import 'package:at_utils/at_utils.dart'; 4 | import 'package:io/ansi.dart'; 5 | import 'package:args/args.dart'; 6 | Future main(List args) async { 7 | 8 | // ArgParser, look for -a , and --rootUrl defaults to root.atsign.org:64, and --verbose or -v for verbose output 9 | final parser = ArgParser() 10 | ..addOption('atSign', abbr: 'a', help: 'The atSign to use') 11 | ..addOption('rootUrl', defaultsTo: 'root.atsign.org:64', help: 'The root URL to connect to') 12 | ..addOption('keys-file', abbr: 'k', help: 'Path to the atKeys file') 13 | ..addFlag('verbose', abbr: 'v', defaultsTo: false, help: 'Enable verbose output') 14 | ..addFlag('help', abbr: 'h', defaultsTo: false, help: 'Show this help message'); 15 | 16 | late ArgResults results; 17 | try { 18 | results = parser.parse(args); 19 | } catch (e) { 20 | print('Error: $e'); 21 | print(''); 22 | print('Usage: at_repl [options]'); 23 | print(''); 24 | print('Options:'); 25 | print(parser.usage); 26 | return; 27 | } 28 | 29 | // Check if help flag is set 30 | if (results['help'] as bool) { 31 | print('Usage: at_repl [options]'); 32 | print(''); 33 | print('Options:'); 34 | print(parser.usage); 35 | return; 36 | } 37 | 38 | // Check if required atSign is provided 39 | final String? atSign = results['atSign'] as String?; 40 | if (atSign == null || atSign.isEmpty) { 41 | print('Error: atSign is required'); 42 | print(''); 43 | print('Usage: at_repl [options]'); 44 | print(''); 45 | print('Options:'); 46 | print(parser.usage); 47 | return; 48 | } 49 | String rootUrl = results['rootUrl'] as String; 50 | final String? keysFile = results['keys-file'] as String?; 51 | final bool verbose = results['verbose'] as bool; 52 | 53 | if (!rootUrl.contains(':')) { 54 | rootUrl = '$rootUrl:64'; 55 | } 56 | 57 | String rootDomain = rootUrl.split(':')[0]; 58 | int rootPort = int.parse(rootUrl.split(':')[1]); 59 | 60 | AtSignLogger.root_level = verbose ? 'info' : 'shout'; 61 | 62 | REPL repl = REPL(); 63 | repl.outputStream.writeln(blue.wrap("Starting at_repl with atSign: $atSign ($rootDomain:$rootPort) ...")); 64 | await repl.authenticate(rootDomain: rootDomain, rootPort: rootPort, atSign: atSign, keysFile: keysFile); 65 | repl.start(); 66 | } 67 | 68 | -------------------------------------------------------------------------------- /packages/at_cli/lib/src/config_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:at_utils/at_utils.dart'; 4 | import 'package:yaml/yaml.dart'; 5 | import 'package:yaml_writer/yaml_writer.dart'; 6 | 7 | class ConfigUtil { 8 | static late final ApplicationConfiguration appConfig; 9 | 10 | static Future init() async { 11 | String configPath = await _getConfigFile(); 12 | appConfig = ApplicationConfiguration(configPath); 13 | } 14 | 15 | static YamlMap? getYaml() { 16 | return appConfig.getYaml(); 17 | } 18 | 19 | static Future _getConfigFile() async { 20 | // Generate the path to the config file which will exist at: 21 | // ~/.atsign/at_cli/config.yaml 22 | String? filePath = getHomeDirectory(); 23 | if (filePath == null) { 24 | throw Exception('Unable to resolve the home directory.'); 25 | } 26 | filePath = '$filePath/.atsign/at_cli/config.yaml'; 27 | 28 | // If the config file doesn't exist, generate a default one. 29 | if (!await File(filePath).exists()) { 30 | await _generateConfigFile(filePath); 31 | } 32 | 33 | return filePath; 34 | } 35 | 36 | static Future _generateConfigFile(String path) async { 37 | File f = File(path); 38 | 39 | await f.create(recursive: true); 40 | await f.writeAsString(defaultConfigYaml); 41 | } 42 | 43 | // Get the home directory or null if unknown. 44 | // From atsign-foundation/at_talk 45 | static String? getHomeDirectory() { 46 | switch (Platform.operatingSystem) { 47 | case 'linux': 48 | case 'macos': 49 | return Platform.environment['HOME']; 50 | case 'windows': 51 | return Platform.environment['USERPROFILE']; 52 | case 'android': 53 | // Probably want internal storage. 54 | return '/storage/sdcard0'; 55 | case 'ios': 56 | // iOS doesn't really have a home directory. 57 | return null; 58 | case 'fuchsia': 59 | // I have no idea. 60 | return null; 61 | default: 62 | return null; 63 | } 64 | } 65 | } 66 | 67 | String defaultConfigYaml = YAMLWriter().write( 68 | { 69 | 'root_server': { 70 | 'port': 64, 71 | 'host': 'root.atsign.org', 72 | }, 73 | 'log': { 74 | 'debug': false, 75 | }, 76 | 'auth': { 77 | 'required': true, 78 | 'mode': 'pkam', 79 | 'key_file_location': '~/.atsign/keys/@alice.atKeys', 80 | 'at_sign': '@alice', 81 | }, 82 | }, 83 | ); 84 | -------------------------------------------------------------------------------- /packages/at_dump_atKeys/bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:args/args.dart'; 6 | import 'package:at_dump_atKeys/commandline_parser.dart'; 7 | import 'package:crypton/crypton.dart'; 8 | import 'package:encrypt/encrypt.dart'; 9 | 10 | var parser = ArgParser(); 11 | 12 | Future main(List arguments) async { 13 | try { 14 | parser = CommandLineParser().getParser(); 15 | if (arguments.length == 1 && 16 | (arguments[0] == '-h' || arguments[0] == '--help') || 17 | arguments.isEmpty) { 18 | print('Usage: \ndart run bin/main.dart \n${parser.usage}'); 19 | exit(0); 20 | } 21 | var args = CommandLineParser().getParserResults(arguments, parser); 22 | var filePath = args['file_path']; 23 | var keyMap = {}; 24 | var isFileExists = await File(filePath).exists(); 25 | if (!isFileExists) { 26 | throw Exception('File not found'); 27 | } 28 | var fileContents = File(filePath).readAsStringSync(); 29 | var keysJSON = json.decode(fileContents); 30 | var aesEncryptionKey = keysJSON['selfEncryptionKey']; 31 | String? enrollmentId = keysJSON['enrollmentId']; 32 | String? apkamSymmetrickey = keysJSON['apkamSymmetricKey']; 33 | keyMap['pkamPublicKey'] = RSAPublicKey.fromString( 34 | decryptValue(keysJSON['aesPkamPublicKey'], aesEncryptionKey)); 35 | keyMap['pkamPrivateKey'] = RSAPrivateKey.fromString( 36 | decryptValue(keysJSON['aesPkamPrivateKey'], aesEncryptionKey)); 37 | keyMap['encryptionPublicKey'] = RSAPublicKey.fromString( 38 | decryptValue(keysJSON['aesEncryptPublicKey'], aesEncryptionKey)); 39 | keyMap['encryptionPrivateKey'] = RSAPrivateKey.fromString( 40 | decryptValue(keysJSON['aesEncryptPrivateKey'], aesEncryptionKey)); 41 | keyMap['selfEncryptionKey'] = aesEncryptionKey; 42 | if ((enrollmentId != null && enrollmentId.isNotEmpty) && 43 | (apkamSymmetrickey != null && apkamSymmetrickey.isNotEmpty)) { 44 | keyMap['enrollmentId'] = enrollmentId; 45 | keyMap['apkamSymmetricKey'] = apkamSymmetrickey; 46 | } 47 | keyMap.forEach((k, v) => print("$k: $v\n")); 48 | } on ArgParserException catch (e) { 49 | print('$e'); 50 | } on Exception catch (e) { 51 | print('Exception : $e'); 52 | } 53 | } 54 | 55 | String decryptValue(String encryptedValue, String decryptionKey) { 56 | var aesKey = AES(Key.fromBase64(decryptionKey)); 57 | var decrypter = Encrypter(aesKey); 58 | var iv2 = IV(Uint8List(16)); 59 | return decrypter.decrypt64(encryptedValue, iv: iv2); 60 | } 61 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/monitor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:at_client/at_client.dart'; 4 | import 'package:io/ansi.dart'; 5 | import '../constants.dart'; 6 | import '../interactive_session.dart'; 7 | 8 | bool get isInMonitorMode => throw UnsupportedError("Use session-based approach instead"); 9 | 10 | class MonitorSession implements InteractiveSession { 11 | final AtClient _atClient; 12 | final String _regex; 13 | final bool _shouldDecrypt; 14 | final IOSink _output; 15 | bool _isActive = true; 16 | 17 | MonitorSession(this._atClient, {String? regex, bool shouldDecrypt = true, IOSink? output}) 18 | : _regex = regex ?? defaultMonitorRegex, 19 | _shouldDecrypt = shouldDecrypt, 20 | _output = output ?? stdout { 21 | 22 | _output.writeln(green.wrap( 23 | "Starting monitor${_regex != defaultMonitorRegex ? ' with regex: $_regex' : ' (excluding statsNotification)'}...")); 24 | _output.writeln(cyan.wrap("Type 'q' to stop monitoring")); 25 | 26 | Stream stream = _atClient.notificationService 27 | .subscribe(regex: _regex, shouldDecrypt: _shouldDecrypt); 28 | stream.listen(_onData); 29 | 30 | _output.writeln(lightYellow.wrap( 31 | "Monitor session started. Using regex: '$_regex'. Waiting for notifications...")); 32 | } 33 | 34 | void _onData(AtNotification atNotification) { 35 | if (!_isActive) return; 36 | 37 | _output.writeln(yellow.wrap('\nRaw notification:')); 38 | _output.writeln(atNotification); 39 | if (_shouldDecrypt && atNotification.value != null) { 40 | _output.writeln(green.wrap('Decrypted value:')); 41 | _output.writeln(atNotification.value!); 42 | final Map jsonValue = 43 | jsonDecode(atNotification.value!); 44 | _output.writeln(cyan.wrap('JSON formatted value:')); 45 | _output.writeln(jsonValue); 46 | } 47 | } 48 | 49 | @override 50 | bool handleInput(String input) { 51 | if (!_isActive) return false; 52 | 53 | input = input.trim().toLowerCase(); 54 | 55 | if (input == 'q' || input == 'quit') { 56 | _output.writeln(green.wrap("Monitor session stopped")); 57 | exit(); 58 | return false; 59 | } 60 | 61 | _output.writeln(cyan.wrap("Type 'q' to quit monitoring")); 62 | return true; 63 | } 64 | 65 | @override 66 | String getPrompt() { 67 | return "monitor> "; 68 | } 69 | 70 | @override 71 | bool get isActive => _isActive; 72 | 73 | @override 74 | void exit() { 75 | _isActive = false; 76 | _atClient.notificationService.stopAllSubscriptions(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/at_repl/decisions/repl.md: -------------------------------------------------------------------------------- 1 | # REPL 2 | 3 | The goal of REPL is to provide an interactive interface for people to interact with their atServer. 4 | 5 | Once staretd via `dart run bin/at_repl.dart -a `, you can do one of two things 6 | 7 | 1. Pass a raw atProtocol command (such as `scan`, `llookup`, `plookup`, `update` + '\n' etc) which will send a raw command to the atServer and print the response. 8 | 2. Use the REPL commands which are prefixed with `/` to interact with the atServer which are commands that use the AtClient object to interact with the atServer. 9 | An atServer is just like a key-value store database, so you can use the REPL commands to get, put, delete, scan, monitor and inspect keys in the atServer. 10 | 11 | ## REPL Commands 12 | 13 | - `/scan [regex]` - Scans for atKeys that match the regex. Defaults to `.*` if no regex is provided. 14 | - `/get ` - Gets the value of the atKey. 15 | - `/put ` - Puts the value into the atKey. 16 | - `/delete ` - Deletes the atKey. 17 | - `/monitor [regex]` - Monitors for atKeys that match the regex. Defaults to omitting the following list of strings: `['statsNotification']`. 18 | - `/help` - Prints the usage of the REPL commands. 19 | - `/inspect [regex]` - inspects AtKeys and gives you an interactive experience where you can enter an index and either v (view) or d (delete) the AtKey. By default, the regex omits the following list of strings: `['shared_key', 'signing_privatekey', 'signing_publickey', 'publickey']`. When it prompts you to enter an index, you can also enter 'l' to relist the keys or 'q' to quit the interactive mode. 20 | - `/inspect_notify` - Uses `notify:list` to receive notifications and allows you to inspect them interactively using similar logic to inspect, and also allows you to delete notifications. 21 | 22 | ## Interactive Mode 23 | 24 | There are different REPL or Interactive modes that you can enter: 25 | 26 | - **Main Mode**: This is the default mode where you can enter any of the REPL commands. 27 | - **Inspect Keys Mode**: This mode is entered when you use the `/inspect` command. 28 | - **Inspect Notifications Mode**: This mode is entered when you use the `/inspect_notify` command. 29 | - **Monitor Mode**: This mode is entered when you use the `/monitor` command. 30 | 31 | Each of these modes `implements` the interactive session interface 32 | 33 | ## Default Regexes 34 | 35 | We omit certain keys from the default regexes for the following reasons: 36 | 37 | 1. For monitor mode, we default regex to omit statsNotification because the atServer automatically sends stats notifications which are not useful for the user. 38 | 2. For inspect keys mode, we default regex to omit the following list of strings: `['shared_key', 'signing_privatekey', 'signing_publickey', 'publickey']` because these keys are not useful for the user to inspect, and they are used for the atClient to function properly. Generally, these should not be deleted or tampered with unless you know what you are doing. 39 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/help.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:io/ansi.dart'; 3 | 4 | void printUsage([IOSink? stream]) { 5 | final output = stream ?? stdout; 6 | 7 | output.writeln(lightRed.wrap("\n AtClient REPL")); 8 | output.writeln("Notes:"); 9 | output.writeln( 10 | " 1) By default, REPL treats input as atProtocol commands. Use / for additional commands listed below"); 11 | 12 | output.write( 13 | " 2) In the usage examples below, it is assumed that the atSign being used is "); 14 | output.writeln(green.wrap("@alice \n")); 15 | output.write(magenta.wrap(" help or /help")); 16 | output.writeln("- print this help message \n"); 17 | 18 | output.write(magenta.wrap("/scan")); 19 | output.write(green.wrap(" [regex] ")); 20 | output.writeln( 21 | "- scan for all records, or all records whose keyNames match the regex (e.g. /scan test@alice.*) \n"); 22 | 23 | output.write(magenta.wrap("/put")); 24 | output.write(green.wrap(" ")); 25 | output.write(lightBlue.wrap(" ")); 26 | output.writeln( 27 | "- create or update a record with the given atKeyName and with the supplied value \n For example: "); 28 | 29 | output.write(magenta.wrap(" /put")); 30 | output.write(green.wrap(" test@alice ")); 31 | output.write(lightBlue.wrap(" secrets ")); 32 | output.writeln( 33 | "-> will create or update a 'self' record (a record private just to @alice)"); 34 | 35 | output.write(magenta.wrap(" /put")); 36 | output.write(green.wrap(" @bob:test@alice ")); 37 | output.write(lightBlue.wrap(" Hello, Bob! ")); 38 | output.writeln( 39 | "-> will create or update a record encrypted for, and then shared with, @bob \n"); 40 | 41 | output.write(magenta.wrap("/get")); 42 | output.write(green.wrap(" ")); 43 | output.writeln( 44 | "- retrieve a value from the record with this atKeyName \n For example: "); 45 | 46 | output.write(magenta.wrap(" /get")); 47 | output.write(green.wrap(" test@alice ")); 48 | output.writeln("- retrieve a value from test@alice. \n"); 49 | 50 | output.write(magenta.wrap("/delete")); 51 | output.write(green.wrap(" ")); 52 | output.writeln( 53 | "- delete the record with this atKeyName (e.g. /delete test@alice) \n"); 54 | 55 | output.write(magenta.wrap("/q or /quit")); 56 | output.writeln("- will quit the REPL \n"); 57 | 58 | output.write(magenta.wrap("/inspect")); 59 | output.write(green.wrap(" [regex] ")); 60 | output.writeln( 61 | "- enter inspect mode to browse and manage AtKeys, optionally filtered by regex (default: excludes shared_key, publickey, and signing_privatekey) \n"); 62 | 63 | output.write(magenta.wrap("/inspect_notify")); 64 | output.writeln( 65 | "- enter notification inspect mode to browse and manage notifications (view/delete) \n"); 66 | 67 | output.write(magenta.wrap("/monitor")); 68 | output.write(green.wrap(" [regex] ")); 69 | output.writeln( 70 | "- start monitoring notifications from the atServer with optional regex filtering (default: ignores statsNotification) \n"); 71 | } -------------------------------------------------------------------------------- /packages/at_cli/bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:args/args.dart'; 3 | import 'package:at_cli/at_cli.dart'; 4 | import 'package:at_cli/src/command_line_parser.dart'; 5 | import 'package:at_cli/src/preference.dart'; 6 | import 'package:at_cli/src/config_util.dart'; 7 | import 'package:at_utils/at_logger.dart'; 8 | 9 | void main(List arguments) async { 10 | AtSignLogger.root_level = 'severe'; 11 | await ConfigUtil.init(); 12 | try { 13 | var parsedArgs = CommandLineParser.getParserResults(arguments); 14 | if (parsedArgs == null || parsedArgs.arguments.isEmpty) { 15 | print('Usage: \n${CommandLineParser.getUsage()}'); 16 | exit(0); 17 | } 18 | var currentAtSign = _getCurrentAtSign(parsedArgs).trim(); 19 | var preferences = await _getAtCliPreference(parsedArgs); 20 | if (!(await File(preferences.authKeyFile).exists())) { 21 | print('Given Authentication key file is not exists. ' 22 | '\nPlease update correct file path in config or provide as commandline argument'); 23 | print('Usage: \n ${CommandLineParser.getUsage()}'); 24 | exit(0); 25 | } 26 | await AtCli.getInstance().init(currentAtSign, preferences); 27 | dynamic result; 28 | // If a verb is provided, call execute method with arguments 29 | // Else if command is provided as argument, we'll execute command directly. 30 | if (parsedArgs['verb'] != null) { 31 | result = await AtCli.getInstance().execute(preferences, parsedArgs); 32 | } else if (parsedArgs['command'] != null) { 33 | var command = parsedArgs['command']; 34 | var auth = parsedArgs['auth']; 35 | result = await AtCli.getInstance() 36 | .executeCommand(currentAtSign, preferences, command, isAuth: auth); 37 | } else { 38 | print('Invalid command. Verb or command not entered'); 39 | } 40 | result = 41 | (result != null) ? result.toString().replaceFirst('data:', '') : result; 42 | print(result); 43 | exit(0); 44 | } on Exception catch (exception) { 45 | print('Exception while running verb : ${exception.toString()}'); 46 | } 47 | } 48 | 49 | String _getCurrentAtSign(ArgResults arguments) { 50 | return ((arguments['atsign'] != null) 51 | ? arguments['atsign'] 52 | : ConfigUtil.getYaml()!['auth']['at_sign']); 53 | } 54 | 55 | String getCommand(ArgResults arguments) { 56 | return arguments.toString(); 57 | } 58 | 59 | Future _getAtCliPreference(ArgResults? parsedArgs) async { 60 | var rootDomainFromConfig = ConfigUtil.getYaml()!['root_server']['host']; 61 | var preferences = AtCliPreference(); 62 | if (rootDomainFromConfig != null) { 63 | preferences.rootDomain = rootDomainFromConfig.trim(); 64 | } 65 | 66 | preferences.authRequired = parsedArgs!['auth']; 67 | 68 | preferences.authMode = ((parsedArgs['mode'] != null) 69 | ? parsedArgs['mode'] 70 | : ConfigUtil.getYaml()!['auth']['mode']); 71 | 72 | preferences.authKeyFile = ((parsedArgs['authKeyFile'] != null) 73 | ? parsedArgs['authKeyFile'] 74 | : ConfigUtil.getYaml()!['auth']['key_file_location']); 75 | 76 | return preferences; 77 | } 78 | -------------------------------------------------------------------------------- /.github/workflows/scorecards.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecards supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '22 4 * * 6' 14 | push: 15 | branches: [ "trunk" ] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecards analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | # Uncomment the permissions below if installing in a private repository. 30 | # contents: read 31 | # actions: read 32 | 33 | steps: 34 | - name: "Checkout code" 35 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 36 | with: 37 | persist-credentials: false 38 | 39 | - name: "Run analysis" 40 | uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 41 | with: 42 | results_file: results.sarif 43 | results_format: sarif 44 | # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if: 45 | # - you want to enable the Branch-Protection check on a *public* repository, or 46 | # - you are installing Scorecards on a *private* repository 47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 48 | repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} 49 | 50 | # Public repositories: 51 | # - Publish results to OpenSSF REST API for easy access by consumers 52 | # - Allows the repository to include the Scorecard badge. 53 | # - See https://github.com/ossf/scorecard-action#publishing-results. 54 | # For private repositories: 55 | # - `publish_results` will always be set to `false`, regardless 56 | # of the value entered here. 57 | publish_results: true 58 | 59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 60 | # format to the repository Actions tab. 61 | - name: "Upload artifact" 62 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 63 | with: 64 | name: SARIF file 65 | path: results.sarif 66 | retention-days: 5 67 | 68 | # Upload the results to GitHub's code scanning dashboard. 69 | - name: "Upload to code-scanning" 70 | uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 71 | with: 72 | sarif_file: results.sarif 73 | -------------------------------------------------------------------------------- /packages/at_cram/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | ## at_cram 4 | 5 | The at_cram utility is used to create the authentication digest when connecting 6 | to a secondary server that does not yet have pkam authentication activated. 7 | 8 | When a secondary is started up for the first time it is started with a shared 9 | secret via the commandline. That shared secret is shared with the person who 10 | owns the atSign of the secondary. This is normally done on both the 11 | [atsign.com](atsign.com) website and when using 12 | [dess](https://github.com/atsign-foundation/dess) via the use of a QRCode. 13 | 14 | When using the [Virtual Environment](https://github.com/atsign-foundation/at_virtual_environment) (VE) the 15 | shared secrets can be found [here](https://github.com/atsign-foundation/at_tools/tree/trunk/at_cram/cramkeys) 16 | for each of the preconfigured atSigns in VE. 17 | 18 | The cram: atProtocol verb is only used to bootstrap a new secondary so that the public keys can be transfered to the secondary. Once the pkam keys have been set the cram process is disabled. The means that before the secondary is used for the atSigns data the cram is disabled and the pkam used going forward. 19 | 20 | To use the at_cram utility connect to a new secondary with a known shared secret, enter the from: verb 21 | and the atSign to authenticate as then use the cram: verb with the result of the at_cram binary. 22 | To use the at_cram tool run `dart bin/at_cram ` then cut and paste the 23 | result in to the cram: verb. 24 | 25 | For example when using VE 26 | 27 | [![asciicast](https://asciinema.org/a/4YBCRUt4duFs9u4fAEfmMhhAS.svg)](https://asciinema.org/a/4YBCRUt4duFs9u4fAEfmMhhAS) 28 | 29 | ### at_cram_single 30 | The `at_cram_single` dart program can cram digest all in a single command. Simply: 31 | 1. `dart bin/at_cram_single.dart -k -c ` 32 | 2. Program will output digest (Use the digest via `cram:` in the @protocol) 33 | 34 | Or compile the program: 35 | 1. Compile `dart compile exe bin/at_cram_single.dart -o cram` 36 | 2. Run `./cram -k -c ` 37 | 3. Program will output digest (Use the digest via `cram:` in the @protocol) 38 | 39 | Here we connect to the @colin🛠 secondary on port 25004 using openssl and then issue the from: verb with 40 | @colin🛠 as the argument, the challenge is given back in return. This challenge is then put into the 41 | at_cram tool and a digest given back. This digest in then entered using the cram: verb and it is successful 42 | as the @colin🛠@ prompt is returned. 43 | 44 | ## How this works 45 | 46 | The shared secret is known to the person and the secondary but we do not want to send it across the wire. 47 | The solution is adding the shared secret to the challenge and then sending over a SHA hash of the resulting string. This is what the at_cram binary does, it is not complex at all, but very effective. 48 | 49 | Created from templates made available by Stagehand under a BSD-style 50 | [license](https://github.com/dart-lang/stagehand/blob/master/LICENSE). 51 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | at_repl - Dart API docs 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | menu 31 | 35 |
at_repl
36 | 39 |
40 | 49 |
50 |
51 |
52 | 53 |
54 |
55 | 56 | 76 | 77 | 79 | 80 |
81 | 82 |
83 | 84 | at_repl 85 | 1.0.0 86 | 87 | 88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | # Contributing guidelines 4 | 5 | We 💙 [Pull Requests](https://help.github.com/articles/about-pull-requests/) 6 | for fixing issues or adding features. Thanks for your contribution! 7 | 8 | Please read our [code of conduct](code_of_conduct.md), which is based on 9 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](code_of_conduct.md) 10 | 11 | 12 | For small changes, especially documentation, you can simply use the "Edit" button 13 | to update the Markdown file, and start the 14 | [pull request](https://help.github.com/articles/about-pull-requests/) process. 15 | Use the preview tab in GitHub to make sure that it is properly 16 | formatted before committing. Please use conventional commits and follow the semantic PR format as documented 17 | [here](https://github.com/atsign-foundation/.github/blob/trunk/atGitHub.md#semantic-prs). 18 | A pull request will cause integration tests to run automatically, so please review 19 | the results of the pipeline and correct any mistakes that are reported. 20 | 21 | If you plan to contribute often or have a larger change to make, it is best to 22 | setup an environment for contribution, which is what the rest of these guidelines 23 | describe. The atsign-foundation GitHub organization's conventions and configurations are documented 24 | [here](https://github.com/atsign-foundation/.github/blob/trunk/atGitHub.md). 25 | 26 | ## Development Environment Setup 27 | 28 | 29 | ### Prerequisites 30 | 31 | ``` sh 32 | # show how to install the tools needed to work with the code here 33 | ``` 34 | 35 | 36 | ### GitHub Repository Clone 37 | 38 | To prepare your dedicated GitHub repository: 39 | 40 | 1. Fork in GitHub https://github.com/atsign-foundation/at_tools 41 | 2. Clone *your forked repository* (e.g., `git clone git@github.com:yourname/at_tools`) 42 | 3. Set your remotes as follows: 43 | 44 | ```sh 45 | cd at_tools 46 | git remote add upstream git@github.com:atsign-foundation/at_tools.git 47 | git remote set-url upstream --push DISABLED 48 | ``` 49 | 50 | Running `git remote -v` should give something similar to: 51 | 52 | ```text 53 | origin git@github.com:yourname/at_tools.git (fetch) 54 | origin git@github.com:yourname/at_tools.git (push) 55 | upstream git@github.com:atsign-foundation/at_tools.git (fetch) 56 | upstream DISABLED (push) 57 | ``` 58 | 59 | The use of `upstream --push DISABLED` is to prevent those 60 | with `write` access to the main repository from accidentally pushing changes 61 | directly. 62 | 63 | ### Development Process 64 | 65 | 1. Fetch latest changes from main repository: 66 | 67 | ```sh 68 | git fetch upstream 69 | ``` 70 | 71 | 1. Reset your fork's `trunk` branch to exactly match upstream `trunk`: 72 | 73 | ```sh 74 | git checkout trunk 75 | git reset --hard upstream/trunk 76 | git push --force 77 | ``` 78 | 79 | **IMPORTANT**: Do this only once, when you start working on new feature as 80 | the commands above will completely overwrite any local changes in `trunk` content. 81 | 1. Edit, edit, edit, and commit your changes to Git: 82 | 83 | ```sh 84 | # edit, edit, edit 85 | git add * 86 | git commit -m 'A useful commit message' 87 | git push 88 | ``` 89 | 90 | 1. How to run tests: 91 | 92 | ``` sh 93 | # explain tests here 94 | ``` 95 | 96 | 1. Open a new Pull Request to the main repository using your `trunk` branch -------------------------------------------------------------------------------- /packages/at_pkam/bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:archive/archive_io.dart'; 6 | import 'package:args/args.dart'; 7 | import 'package:at_pkam/commandline_parser.dart'; 8 | import 'package:at_pkam/pkam_constants.dart'; 9 | import 'package:crypton/crypton.dart'; 10 | import 'package:encrypt/encrypt.dart'; 11 | 12 | var parser = ArgParser(); 13 | 14 | Future main(List arguments) async { 15 | try { 16 | parser = CommandLineParser().getParser(); 17 | if (arguments.length == 1 && 18 | (arguments[0] == '-h' || arguments[0] == '--help')) { 19 | print('Usage: \ndart bin/main.dart \n${parser.usage}'); 20 | exit(0); 21 | } 22 | var args = CommandLineParser().getParserResults(arguments, parser); 23 | var filePath = args['file_path']; 24 | var challenge = args['from_response']; 25 | var aesKeyFile = args['aes_key']; 26 | var privateKey; 27 | if (filePath.endsWith(AT_KEYS)) { 28 | privateKey = await getSecretFromAtKeys(filePath); 29 | } else if (filePath.endsWith(ZIP)) { 30 | privateKey = await getSecretFromZip(filePath, aesKeyFile); 31 | } else { 32 | print('Usage : \n${parser.usage}'); 33 | exit(0); 34 | } 35 | if (privateKey != null) { 36 | privateKey = privateKey.trim(); 37 | var key = RSAPrivateKey.fromString(privateKey); 38 | challenge = challenge.trim(); 39 | var signature = 40 | base64.encode(key.createSHA256Signature(utf8.encode(challenge))); 41 | stdout.write(signature); 42 | stdout.write('\n'); 43 | } 44 | } on ArgParserException catch (e) { 45 | print('$e'); 46 | } on Exception catch (e) { 47 | print('Exception : $e'); 48 | } 49 | } 50 | 51 | Future getSecretFromAtKeys(String filePath) async { 52 | try { 53 | var isFileExists = await File(filePath).exists(); 54 | if (!isFileExists) { 55 | throw Exception('File not found'); 56 | } 57 | var fileContents = File(filePath).readAsStringSync(); 58 | var keysJSON = json.decode(fileContents); 59 | var encryptedPKAMPrivateKey = keysJSON['aesPkamPrivateKey']; 60 | var aesEncryptionKey = keysJSON['selfEncryptionKey']; 61 | var pkamPrivateKey = 62 | decryptValue(encryptedPKAMPrivateKey, aesEncryptionKey); 63 | return pkamPrivateKey; 64 | } on Exception catch (e) { 65 | print('Exception while getting secret : $e'); 66 | return null; 67 | } 68 | } 69 | 70 | Future getSecretFromZip(String filePath, String aesKeyFilePath) async { 71 | try { 72 | var isFileExists = await File(filePath).exists(); 73 | if (!isFileExists) { 74 | throw Exception('keys zip file not found'); 75 | } 76 | late var fileContents; 77 | var bytes = File(filePath).readAsBytesSync(); 78 | final archive = ZipDecoder().decodeBytes(bytes); 79 | for (var file in archive) { 80 | if (file.name.contains('atKeys')) { 81 | fileContents = String.fromCharCodes(file.content); 82 | } 83 | } 84 | var keysJSON = json.decode(fileContents); 85 | var encryptedPKAMPrivateKey = keysJSON['aesPkamPrivateKey']; 86 | var isAesFileExists = await File(aesKeyFilePath).exists(); 87 | if (!isAesFileExists) { 88 | throw Exception( 89 | 'aes key file path not provided \nUsage: \n${parser.usage}'); 90 | } 91 | var aesKey = File(aesKeyFilePath).readAsStringSync(); 92 | aesKey = aesKey.trim(); 93 | var pkamPrivateKey = decryptValue(encryptedPKAMPrivateKey, aesKey); 94 | return pkamPrivateKey; 95 | } on Exception catch (e) { 96 | print('Exception while getting secret : $e'); 97 | return null; 98 | } 99 | } 100 | 101 | String decryptValue(String encryptedValue, String decryptionKey) { 102 | var aesKey = AES(Key.fromBase64(decryptionKey)); 103 | var decrypter = Encrypter(aesKey); 104 | var iv2 = IV(Uint8List(16)); 105 | return decrypter.decrypt64(encryptedValue, iv: iv2); 106 | } 107 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/__404error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | at_repl - Dart API docs 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | menu 31 | 34 |
at_repl
35 | 38 |
39 | 48 |
49 |
50 |
51 | 52 |
53 |

404: Something's gone wrong :-(

54 | 55 |
56 |

You've tried to visit a page that doesn't exist. Luckily this site 57 | has other pages.

58 |

If you were looking for something specific, try searching: 59 |

62 |

63 | 64 |
65 |
66 | 67 | 86 | 87 | 89 | 90 |
91 | 92 |
93 | 94 | at_repl 95 | 1.0.0 96 | 97 | 98 | 99 |
100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /packages/at_repl/doc/api/at_repl/at_repl-library.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | at_repl library - Dart API 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 | menu 30 | 34 |
at_repl
35 | 38 |
39 | 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 |

at_repl library 56 | 57 |

58 | 59 | 60 |
61 |
# activate at_repl
 62 | $ dart pub global activate at_repl
 63 | 
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
77 | 78 | 98 | 99 | 113 | 114 |
115 | 116 |
117 | 118 | at_repl 119 | 1.0.0 120 | 121 | 122 | 123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /packages/at_ve_doctor/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | ## at_ve_doctor 4 | 5 | at_ve_doctor is a simple utility than can check the state of the secondaries 6 | running in the virtual environment (VE). 7 | The virtual environment provides the full atPlatform stack including an atRoot 8 | server and a number of preconfigured atSigns. 9 | 10 | Once the virtual environment is up and running the at_ve_doctor can be run 11 | and it will report back the state of each of the preconfigured atSigns. 12 | 13 | Before the atSigns are paired with a device they will report as being in a 14 | "teapot" once paired and a pkam key is in place they will report as 15 | "activated". 16 | 17 | Activation can be done via the onboarding widget or if you prefer to 18 | activate all the atSigns with predefined pkam keys the pkamLoad script 19 | can be run on the supervisor Ui found at localhost:9001 when running the VE. 20 | 21 | ``` 22 | $ dart bin/at_ve_doctor.dart 23 | @alice🛠 status: AtSignStatus.teapot 24 | outbound finish handler called 25 | @ashish🛠 status: AtSignStatus.teapot 26 | outbound finish handler called 27 | @barbara🛠 status: AtSignStatus.teapot 28 | outbound finish handler called 29 | @bob🛠 status: AtSignStatus.teapot 30 | outbound finish handler called 31 | @colin🛠 status: AtSignStatus.teapot 32 | outbound finish handler called 33 | @egbiometric🛠 status: AtSignStatus.teapot 34 | outbound finish handler called 35 | @egcovidlab🛠 status: AtSignStatus.teapot 36 | outbound finish handler called 37 | @egcreditbureau🛠 status: AtSignStatus.teapot 38 | outbound finish handler called 39 | @eggovagency🛠 status: AtSignStatus.teapot 40 | outbound finish handler called 41 | @emoji🦄🛠 status: AtSignStatus.teapot 42 | outbound finish handler called 43 | @eve🛠 status: AtSignStatus.teapot 44 | outbound finish handler called 45 | @jagan🛠 status: AtSignStatus.teapot 46 | outbound finish handler called 47 | @kevin🛠 status: AtSignStatus.teapot 48 | outbound finish handler called 49 | @murali🛠 status: AtSignStatus.teapot 50 | outbound finish handler called 51 | @naresh🛠 status: AtSignStatus.teapot 52 | outbound finish handler called 53 | @purnima🛠 status: AtSignStatus.teapot 54 | outbound finish handler called 55 | @sameeraja🛠 status: AtSignStatus.teapot 56 | outbound finish handler called 57 | @sitaram🛠 status: AtSignStatus.teapot 58 | $ 59 | ``` 60 | 61 | Once pkamLoad has been run 62 | ``` 63 | $ dart bin/at_ve_doctor.dart 64 | @alice🛠 status: AtSignStatus.activated 65 | outbound finish handler called 66 | @ashish🛠 status: AtSignStatus.activated 67 | outbound finish handler called 68 | @barbara🛠 status: AtSignStatus.activated 69 | outbound finish handler called 70 | @bob🛠 status: AtSignStatus.activated 71 | outbound finish handler called 72 | @colin🛠 status: AtSignStatus.activated 73 | outbound finish handler called 74 | @egbiometric🛠 status: AtSignStatus.activated 75 | outbound finish handler called 76 | @egcovidlab🛠 status: AtSignStatus.activated 77 | outbound finish handler called 78 | @egcreditbureau🛠 status: AtSignStatus.activated 79 | outbound finish handler called 80 | @eggovagency🛠 status: AtSignStatus.activated 81 | outbound finish handler called 82 | @emoji🦄🛠 status: AtSignStatus.activated 83 | outbound finish handler called 84 | @eve🛠 status: AtSignStatus.activated 85 | outbound finish handler called 86 | @jagan🛠 status: AtSignStatus.activated 87 | outbound finish handler called 88 | @kevin🛠 status: AtSignStatus.activated 89 | outbound finish handler called 90 | @murali🛠 status: AtSignStatus.activated 91 | outbound finish handler called 92 | @naresh🛠 status: AtSignStatus.activated 93 | outbound finish handler called 94 | @purnima🛠 status: AtSignStatus.activated 95 | outbound finish handler called 96 | @sameeraja🛠 status: AtSignStatus.activated 97 | outbound finish handler called 98 | @sitaram🛠 status: AtSignStatus.activated 99 | $ 100 | ``` -------------------------------------------------------------------------------- /packages/at_repl/doc/api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | at_repl - Dart API docs 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | menu 31 | 34 |
at_repl
35 | 38 |
39 | 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 |

at_repl

56 |

A CLI application that talks directly to the atPlatform.

57 |

Getting Started

58 |

Ensure you have you atSign keys, keys are usually located in "homeDirectory.atsign\keys".

59 |

If you don't have an atSign, visit here https://my.atsign.com/login.

60 |

If the CLI application is available on pub, activate globally via:

61 |
dart pub global activate at_repl
 62 | 
63 |

Or locally via:

64 |
dart pub global activate --source=path <path to this package>
 65 | 
66 |

Usage

67 |

-a, user's atsign (REQUIRED) 68 | -r, root URL (defaults to root.atsign.org:64) 69 | -v, verbose 70 | -n, enforce namespaces (defaults to false)

71 |
#example of full REPL command
 72 | $ at_repl -a @xavierlin -r root.atsign.org:64 -v -n
 73 | 
 74 | #example of shortened REPL command
 75 | $ at_repl -a @xavierlin
 76 | 
 77 | 
78 |
79 | 80 | 81 |
82 |

Libraries

83 |
84 |
85 | at_repl 86 | 87 |
88 |
# activate at_repl 89 | $ dart pub global activate at_repl 90 | 91 |
92 | 93 |
94 |
95 | 96 |
97 | 98 | 117 | 118 | 120 | 121 |
122 | 123 |
124 | 125 | at_repl 126 | 1.0.0 127 | 128 | 129 | 130 |
131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/inspect_keys.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:io'; 3 | import 'dart:convert'; 4 | import 'package:at_client/at_client.dart'; 5 | import 'package:io/ansi.dart'; 6 | import '../interactive_session.dart'; 7 | import 'get.dart'; 8 | import 'delete.dart'; 9 | 10 | class InspectKeysSession implements InteractiveSession { 11 | final List _keys; 12 | final AtClient _atClient; 13 | final IOSink _outputStream; 14 | bool _isActive = true; 15 | bool _waitingForAction = false; 16 | int _selectedKeyIndex = -1; 17 | 18 | InspectKeysSession(this._keys, this._atClient, this._outputStream) { 19 | _showKeyList(); 20 | _outputStream.writeln(cyan.wrap("\nEntering interactive mode. Type a number to select a key, 'l' to relist, or 'q' to quit.")); 21 | } 22 | 23 | @override 24 | bool handleInput(String input) { 25 | if (!_isActive) return false; 26 | 27 | input = input.trim(); 28 | 29 | if (input.toLowerCase() == 'q' || input.toLowerCase() == 'quit') { 30 | _outputStream.writeln(green.wrap("Exiting interactive mode")); 31 | exit(); 32 | return false; 33 | } 34 | 35 | if (_waitingForAction) { 36 | return _handleActionInput(input); 37 | } 38 | 39 | if (input.toLowerCase() == 'l' || input.toLowerCase() == 'list') { 40 | _showKeyList(); 41 | return true; 42 | } 43 | 44 | final index = int.tryParse(input); 45 | if (index == null || index < 1 || index > _keys.length) { 46 | _outputStream.writeln(red.wrap("Invalid selection. Enter a number 1-${_keys.length}, 'l' to relist, or 'q' to quit.")); 47 | return true; 48 | } 49 | 50 | final selectedKey = _keys[index - 1]; 51 | _outputStream.writeln(green.wrap("Selected: ${selectedKey.toString()}")); 52 | _outputStream.writeln(cyan.wrap("Enter 'v' to view or 'd' to delete:")); 53 | 54 | _waitingForAction = true; 55 | _selectedKeyIndex = index - 1; 56 | 57 | return true; 58 | } 59 | 60 | bool _handleActionInput(String input) { 61 | final action = input.trim().toLowerCase(); 62 | final selectedKey = _keys[_selectedKeyIndex]; 63 | 64 | if (action == 'v') { 65 | _handleViewKey(selectedKey); 66 | } else if (action == 'd') { 67 | _handleDeleteKey(selectedKey, _selectedKeyIndex); 68 | } else { 69 | _outputStream.writeln(red.wrap("Invalid action. Enter 'v' to view or 'd' to delete.")); 70 | return true; 71 | } 72 | 73 | _waitingForAction = false; 74 | _selectedKeyIndex = -1; 75 | 76 | if (_keys.isNotEmpty) { 77 | _outputStream.writeln(cyan.wrap("Type a number to select another key, 'l' to relist, or 'q' to quit.")); 78 | } 79 | 80 | return true; 81 | } 82 | 83 | void _handleViewKey(AtKey key) async { 84 | try { 85 | final value = await get(_atClient, atKeyStr: key.toString()); 86 | if (value != null) { 87 | _outputStream.writeln(green.wrap("Value:")); 88 | _outputStream.writeln(value); 89 | 90 | // Try to format as JSON if it's valid JSON 91 | try { 92 | final jsonValue = jsonDecode(value); 93 | _outputStream.writeln(cyan.wrap("\nFormatted JSON:")); 94 | const JsonEncoder encoder = JsonEncoder.withIndent(' '); 95 | _outputStream.writeln(encoder.convert(jsonValue)); 96 | } catch (e) { 97 | // Not JSON, that's fine 98 | } 99 | } else { 100 | _outputStream.writeln(lightYellow.wrap("Key has no value")); 101 | } 102 | } catch (e) { 103 | _outputStream.writeln(red.wrap("Error getting value: $e")); 104 | } 105 | } 106 | 107 | void _handleDeleteKey(AtKey key, int index) async { 108 | try { 109 | final success = await delete(_atClient, atKeyStr: key.toString()); 110 | if (success) { 111 | _outputStream.writeln(green.wrap("Successfully deleted: ${key.toString()}")); 112 | _keys.removeAt(index); 113 | 114 | if (_keys.isEmpty) { 115 | _outputStream.writeln(lightYellow.wrap("No more keys. Exiting interactive mode.")); 116 | exit(); 117 | return; 118 | } 119 | 120 | _showKeyList(); 121 | } else { 122 | _outputStream.writeln(red.wrap("Failed to delete key")); 123 | } 124 | } catch (e) { 125 | _outputStream.writeln(red.wrap("Error deleting key: $e")); 126 | } 127 | } 128 | 129 | void _showKeyList() { 130 | _outputStream.writeln(green.wrap("\nFound ${_keys.length} key(s):")); 131 | _outputStream.writeln("${'#'.padRight(5)} | Key"); 132 | _outputStream.writeln("${'─' * 5}─┼─${'─' * 50}"); 133 | 134 | for (int i = 0; i < _keys.length; i++) { 135 | final indexStr = (i + 1).toString().padRight(5); 136 | _outputStream.writeln("$indexStr | ${_keys[i].toString()}"); 137 | } 138 | } 139 | 140 | @override 141 | String getPrompt() { 142 | if (_waitingForAction) { 143 | return "action> "; 144 | } 145 | return "inspect_keys> "; 146 | } 147 | 148 | @override 149 | bool get isActive => _isActive; 150 | 151 | @override 152 | void exit() { 153 | _isActive = false; 154 | _waitingForAction = false; 155 | _selectedKeyIndex = -1; 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /packages/at_cli/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | # at_cli 4 | 5 | A command line tool to execute verbs on at platform. 6 | 7 | ### Building 8 | 9 | __Assumption__ - you have the [Dart SDK](https://dart.dev/get-dart) installed. The version should be >= 2.12.0 and <4.0.0. 10 | 11 | First fetch dependencies (as defined in pubspec.yaml): 12 | 13 | ```bash 14 | dart pub get 15 | ``` 16 | 17 | It's now possible to run the command in the Dart VM: 18 | 19 | ```bash 20 | dart run bin/main.dart 21 | ``` 22 | 23 | At which point it will print out some usage instructions: 24 | 25 | ``` 26 | Usage: 27 | -a, --auth Set this flag if command needs auth to server 28 | -m, --mode Choose Authentication mode if auth required 29 | [cram, pkam] 30 | -f, --authKeyFile cram/pkam file path 31 | --atsign Current Atsign 32 | -c, --command The at command to execute 33 | -v, --verb The verb to execute 34 | -p, --public set to true if key has public access 35 | -k, --key key to update 36 | --value value of the key 37 | -w, --shared_with atsign to whom key is shared 38 | -b, --shared_by atsign who shared the key 39 | -r, --regex regex for scan 40 | ``` 41 | 42 | If you're going to run `atcli` a lot then it makes sense to create a binary 43 | and copy it to somewhere on the PATH e.g. 44 | 45 | ```bash 46 | dart compile exe bin/main.dart -o ~/atcli 47 | sudo cp ~/atcli /usr/local/bin/ 48 | ``` 49 | 50 | Verify the following information in config.yaml file. Please provide valid values. 51 | 52 | ``` 53 | root_server: 54 | # The port on which root server runs. 55 | port: 64 56 | host: 'root.atsign.org' 57 | 58 | auth: 59 | required: true # Authentication required for the Verb or not 60 | mode: cram # Mode of Authentication (cram or pkam) 61 | key_file_location: 'bin/@alice' # File path which contains PKAM/CRAM secret 62 | at_sign: '@alice' 63 | ``` 64 | 65 | Also, we can provide auth related information from commandline as mentioned above. 66 | 67 | Here are some examples to execute verbs 68 | 69 | __scan__ 70 | ``` 71 | # Without Authentication 72 | dart run bin/main.dart -v scan 73 | 74 | #This command will return all the keys available publicly for atsign provided in config.yaml 75 | # Alternatively we can provide atSign from commandline as follows 76 | dart run bin/main.dart -v scan -a @alice 77 | 78 | #sample output 79 | [firstname@alice, location@alice, signing_publickey@alice] 80 | ``` 81 | ``` 82 | # With Authentication 83 | dart run bin/main.dart -v scan -a true 84 | or 85 | dart run bin/main.dart -v scan -a true -a @alice 86 | 87 | # This command will return all the keys available for @alice by authenticating using cram/pkam provided in config.yaml 88 | 89 | #sample output 90 | [@alice:signing_privatekey@alice, public:firstname@alice, public:location@alice, public:signing_publickey@alice] 91 | 92 | Note: We can provide Authenication mode using -m/--mode option, cram/pkam file path using -f/--authKeyFile options 93 | ``` 94 | __update__ 95 | 96 | Update verb is used to update/create a key with value. 97 | Update verb required authentication. 98 | ``` 99 | # Update/Create a key 'firstname' with value as 'alice' which is private to alice 100 | dart run bin/main.dart -v update -k firtname --value alice -p false -a true 101 | 102 | # Sample output 103 | true 104 | 105 | # Update/Create a key 'lastname' with value as alice which is available publicly 106 | dart run bin/main.dart -v update -k firtname --value alice -p true -a true 107 | 108 | # Sample output 109 | true 110 | ``` 111 | __lookup__ 112 | 113 | Lookup verb is used to lookup particular key of an atSign. Lookup verb requires authentication. 114 | ``` 115 | # To lookup bob email 116 | dart run bin/main.dart -v lookup -k email --atsign @bob -a true 117 | 118 | # Sample output 119 | bob@at.com 120 | 121 | # You can run command to lookup bob's email as follows 122 | dart run bin/main.dart -c lookup:email@bob -a true 123 | 124 | # Sample output 125 | bob@at.com 126 | ``` 127 | 128 | __llookup__ 129 | 130 | The "llookup" verb can be used to locally lookup keys stored on the secondary server. To execute Llookup authentication is required. 131 | ``` 132 | # To llookup firstname@alice 133 | dart run bin/main.dart -v llookup -k firstname --atsign @alice -a true 134 | 135 | #sample output 136 | alice 137 | 138 | # Alternatively we can run command directly as follows 139 | dart run bin/main.dart -c llookup:firstname@alice -a true 140 | 141 | # sample output 142 | alice 143 | ``` 144 | 145 | __plookup__ 146 | 147 | The "plookup" verb, provides a proxied public lookups for a resolver that perhaps is behind a firewall. This will allow a resolver to contact an atServer and have the atServer lookup both public atSign handles information. 148 | ``` 149 | # To lookup the value of public:location@alice 150 | dart run bin/main.dart -v plookup -k location --atsign @alice -a true 151 | 152 | # Sample output 153 | india 154 | 155 | # We can run plookup command directly as follows 156 | dart run bin/main.dart -c plookup:location@alice -a true 157 | 158 | # Sample output 159 | india 160 | ``` -------------------------------------------------------------------------------- /packages/at_dump_atKeys/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | ## at_dump_atKeys 4 | 5 | A command line tool to dump keys from a .atKeys file 6 | 7 | ### Building 8 | 9 | __Assumption__ - you have the [Dart SDK](https://dart.dev/get-dart) installed. The version should be >= 2.12.0 and <4.0.0. 10 | 11 | First fetch dependencies (as defined in pubspec.yaml): 12 | 13 | ```bash 14 | dart pub get 15 | ``` 16 | 17 | It's now possible to run the command in the Dart VM: 18 | 19 | ```bash 20 | dart run bin/main.dart 21 | ``` 22 | 23 | At which point it will print out some usage instructions: 24 | 25 | ``` 26 | Usage: 27 | dart bin/main.dart 28 | -p, --file_path .atKeys file path which contains keys 29 | ``` 30 | 31 | Starting up the VM every time for a small command line app is a little slow, 32 | so it's better to create a compiled binary: 33 | 34 | ```bash 35 | dart compile exe bin/main.dart -o dump_atKeys 36 | ``` 37 | 38 | The binary can now be run with `./dump_atKeys` along with appropriate arguments. 39 | 40 | ### Usage 41 | 42 | Run the tool against an atKeys file (in this case ~/demo.atKeys): 43 | 44 | ```bash 45 | dart run bin/main.dart -p ~/demo.atKeys 46 | ``` 47 | 48 | or if using a compiled binary: 49 | 50 | ```bash 51 | ./dump_atKeys -p ~/demo.atKeys 52 | ``` 53 | 54 | Which will give output like: 55 | 56 | ``` 57 | pkamPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg1THrbmEpXMDXUtci4GBNaARF2ghWYz6tC5PcO8oyjqioDwlp7lVZcufhgZGD2Dd5j124vyx8t0qlTP/LObK8WvgvLOY5NqUCOgYBv0VufVzOr6xrsNKVEKC9GzMkMbUfFwBVYHeeIg3IKeGTyxB+qHQIAa2gLZCBKX8EsLTGliQmkv2J96umaFTsS/juwxw9nSVpsMIwCcg6hA3njVbjR1p5o9mZ3lVw57GOHzkdyGXXBgKzNvBRXBSDOkNCulxoixrydqWD8sLc8AERQoWCUwSWITTS0YE/jFL8fH2zWupjJ510JmoIf2L2MJsiFFUCtRIRTWSIHs1/cXZiIEvhQIDAQAB 58 | 59 | pkamPrivateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCDVMetuYSlcwNdS1yLgYE1oBEXaCFZjPq0Lk9w7yjKOqKgPCWnuVVly5+GBkYPYN3mPXbi/LHy3SqVM/8s5srxa+C8s5jk2pQI6BgG/RW59XM6vrGuw0pUQoL0bMyQxtR8XAFVgd54iDcgp4ZPLEH6odAgBraAtkIEpfwSwtMaWJCaS/Yn3q6ZoVOxL+O7DHD2dJWmwwjAJyDqEDeeNVuNHWnmj2ZneVXDnsY4fOR3IZdcGArM28FFcFIM6Q0K6XGiLGvJ2pYPywtzwARFChYJTBJYhNNLRgT+MUvx8fbNa6mMnnXQmagh/YvYwmyIUVQK1EhFNZIgezX9xdmIgS+FAgMBAAECggEAEq0z2FjRrFW23MWi25QHNAEXbSS52WpbHNSZJ45bVqcQCYmEMV4B7wAOJ5kszXMRG3USOyWEiO066Q0D9Pa9VafpxewkiicrdjjLcfL76/4j7O7BhgDvyRvMU8ZFMTGVdjn/VpGpeaqlbFdmmkvI9kOcvXE28wb4TIDuYBykuNI6twRqiaVd1LkKg9yoF0DGfSp8OHGWm/wz5wwnNYT6ofTbgV3gSGKOrLf4rC1swHh1VoNXiaYKQQFo2j23vGznC+hVJy8kAkSTMvRy4+SrZ+0MtYrNt0CI9n4hw79BNzwAd0kfJ5WCsYL6MaF8Giyym3Wl77KoiriwRF7cGCEnAQKBgQDWD+l1b6D8QCmrzxI1iZRoehfdlIlNviTxNks4yaDQ/tu6TC/3ySsRhKvwj7BqFYj2A6ULafeh08MfxpG0MfmJ+aJypC+MJixu/z/OXhQsscnR6avQtVLi9BIZV3EweyaD/yN/PB7IVLuhz6E6BV8kfNDb7UFZzrSSlvm1YzIdvQKBgQCdD5KVbcA88xkv/SrBpJcUME31TIR4DZPg8fSB+IDCnogSwXLxofadezH47Igc1CifLSQp4Rb+8sjVOTIoAXZKvW557fSQk3boR3aZ4CkheDznzjq0vY0qot4llkzHdiogaIUdPDwvYBwERzc73CO3We1pHs36bIz70Z3DRF5BaQKBgQC295jUARs4IVu899yXmEYa2yklAz4tDjajWoYHPwhPO1fysAZcJD3E1oLkttzSgB+2MD1VOTkpwEhLE74cqI6jqZV5qe7eOw7FvTT7npxd64UXAEUUurfjNz11HbGo/8pXDrB3o5qoHwzV7RPg9RByrqETKoMuUSk1FwjPSr9efQKBgAdC7w4Fkvu+aY20cMOfLnT6fsA2l3FNf2bJCPrxWFKnLbdgRkYxrMs/JOJTXT+n93DUj3V4OK3036AsEsuStbti4ra0b7g3eSnoE+2tVXl8q6Qz/rbYhKxR919ZgZc/OVdiPbVKUaYHFYSFHmKgHO6fM8DGcdOALUx/NoIOqSTxAoGBALUdiw8iyI98TFgmbSYjUj5id4MrYKXaR7ndS/SQFOBfJWVH09t5bTxXjKxKsK914/bIqEI71aussf5daOHhC03LdZIQx0ZcCdb2gL8vHNTQoqX75bLRN7J+zBKlwWjjrbhZCMLE/GtAJQNbpJ7jOrVeDwMAF8pK+Put9don44Gx 60 | 61 | encryptionPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0sYjWt6TTikajY3HjvdN3sn2Ve3O+i84/gBrWPqhGNSdImz3W2l9dMSHm4wyixsxMSQaL+rECjwnvp3sRdW3M51sDCvWa06MLptvdrtnMjzDrvP45hUJY/i6WeDW8qeEOf9zuo+BLcQ3pkV1KZyhBj80OndLS/y00T8fYB9KnS5Z/iN7KW7Hxuv0isMPXxL1i8AZos7m5GuWq7CfRFKJIZ6vqYBUJCVSQCUVo1llyjElodSywcf1KjCvBOKuMPnUQCs+pKJt3QMFI0U7D+yinnlEdr6TBfOzMMPS3Du1LHpTGt7rqyxZrX8p4kpVb/CyL6wkelMuahHDOeNFBNyF0wIDAQAB 62 | 63 | encryptionPrivateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSxiNa3pNOKRqNjceO903eyfZV7c76Lzj+AGtY+qEY1J0ibPdbaX10xIebjDKLGzExJBov6sQKPCe+nexF1bcznWwMK9ZrTowum292u2cyPMOu8/jmFQlj+LpZ4Nbyp4Q5/3O6j4EtxDemRXUpnKEGPzQ6d0tL/LTRPx9gH0qdLln+I3spbsfG6/SKww9fEvWLwBmizubka5arsJ9EUokhnq+pgFQkJVJAJRWjWWXKMSWh1LLBx/UqMK8E4q4w+dRAKz6kom3dAwUjRTsP7KKeeUR2vpMF87Mww9LcO7UselMa3uurLFmtfyniSlVv8LIvrCR6Uy5qEcM540UE3IXTAgMBAAECggEAfImMLE3k1cLdqJQEPIoNHb1RatZXfGXYk+QliW6VLzm5GrUttnpvIUZaJeNBngXUHAgL3RInATEn/q4LA/xSAhJa3Bou2DqSA5vd0VbLk9hpev82qqP1Z3d4jFCYUMoAC9DPTYUrO6J7iyfxIUQltK41qvH/sIdBQ327iS0UBihhiKg16BOKG4SoFJHZfhhL6m86+jnsaBTaAWb8hpa/Mwqs5eDHF78DHK8o+4Q6DufDi34nCwdxEexL3MFa9L0qGbQAqJshgDcJ6yxUzb5+tw3XXpiE0yG9aZ5gPaS2UgOYY1m2mmF4RjFSiLmKyN99H99ycA59enVFyfYh4SnuMQKBgQDq9IwkVyDkNxkaW6hyYMzBwNqId74JUNjXCWyzDJ58JWaNvFYYY4ujSCLTdfdugmVTIUjqXMVsxzq/e9jNaOj7u27/3inqn1VC88GFJJiUiLQcTP1T5ySP4jy5GVrhQ1zP8PtiRqE34emYfVY8OLa7bwf5CufgbL5RzKPrfIafKQKBgQDlpx8DoETRPE7FyZJg9xiUTyZmI/P6RmhCO86knbQa4hEWiCuEIiOheJQxdcW6yCNImbJNSEFUnpweiHEw4xdMmlpR4JDkvsGOyjLI6Y36Yxbi+AipvTuYZ/La7fuOeEjwD7OlgJmva2jEQL6GlhmTibgt5dfwzOiAP0gC4tXomwKBgQDAnZDSLfeSADV9LU0vz3mtEYxWOkw52OSbjWdmdd7ricHESnUOc3VDe9zJHLmnCBFHEE91im5zWfUoi8BVzT7LOIKsEpaseMjuJWUt4K2Rf2ygkuFPSnvn1SHQ4R9m8tGAy19a1upOJM9bKs1qe1ga2tBfc3havOtdpfVwFVtL2QKBgDFsPehx3V2KNQmrz6y+gLOqNQFWS3NZI6bdaCNVLSV78WF//J17G1/sqzfZuKvx1mYRbaXkHusvFzoa8wEqXiFGNpnYUlZoFw+7xCIo4T05hftilbqx1tl9xW4IOVL33/qJ5od/nZN68hkKNfaQ5wAxa0m1ZTuVXZP8CmtUleRxAoGAQcz+GcrzLybY8EMOhYRbb79H8tQ+SoFkTOmJV4ZQDxzg18dPd8+U3YaMp3QngxncOSpjSwsuqpw/SqxHBQE/v91uEzLfPPS2QJ5COBvXM2Y7PsSmMnukIOM0NrtU8MIonv+l7UsHDeCllqe5+uRPpBUUk2mljPVprXo0SDjQr1U= 64 | 65 | selfEncryptionKey: vR+w/lx9qitj/W2+SfFxbjeRM8VdaYGsxG6lxYCVQ0w= 66 | ``` -------------------------------------------------------------------------------- /packages/at_dump_atKeys/bin/generate_at_demo_data.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:args/args.dart'; 6 | import 'package:at_dump_atKeys/commandline_parser.dart'; 7 | import 'package:crypton/crypton.dart'; 8 | import 'package:encrypt/encrypt.dart'; 9 | 10 | /// The script accepts the path to the directory that contains ".atKeys" file(s), 11 | /// Iterates through all the ".atKeys" files, decrypts the keys and writes to a file. 12 | /// 13 | /// Optionally accepts the following flags: 14 | /// * -o to set the destination directory to create the file. Defaults to the current directory 15 | /// * -f to set the filename. Defaults to at_demo_data.dart 16 | /// * -p This flag indicates to decrypt all keys or only PKAM keys. Defaults to "all" 17 | /// - When set to "all" all the keys are decrypted and written to output file. 18 | /// - Set the flag to "PKAM" to write only PKAM private key to the output file. 19 | /// For the end2end test in at_server, only PKAM keys are needed and for end2end tests 20 | /// in at_client all the keys are needed. 21 | /// 22 | /// Usage: 23 | /// 24 | /// The following command iterates through all the .atKeys file present in "/home/user" directory and 25 | /// writes the decrypted keys to an output file. 26 | /// 27 | /// ```dart 28 | /// dart bin/generate_at_demo_data.dart -d /home/user/atKeys 29 | ///``` 30 | /// 31 | /// With optional flags: 32 | /// The below command places the output file in "/home/user/destDir" and creates a file with name "at_credentials.dart" 33 | /// ```dart 34 | /// dart bin/generate/at_demo_data.dart -d /home/user/atKeys -o /home/user/destDir -f at_credentials.dart 35 | /// ``` 36 | 37 | Future main(List arguments) async { 38 | try { 39 | ArgParser argParser = getArgParser(); 40 | if (arguments.length < 2 || 41 | (arguments[0] == '-h' || arguments[0] == '--help') || 42 | arguments.isEmpty) { 43 | print( 44 | 'Usage:\ndart run bin/generate_at_demo_data.dart \n${argParser.usage}'); 45 | exit(0); 46 | } 47 | var args = CommandLineParser().getParserResults(arguments, argParser); 48 | String dirPath = args['directoryPath']; 49 | String outputDir = args['outputDir']; 50 | String fileName = args['fileName']; 51 | String preference = args['preference'].toString().toLowerCase(); 52 | if (!outputDir.endsWith('/')) { 53 | outputDir = '$outputDir/'; 54 | } 55 | List listOfAtKeys = Directory(dirPath).listSync(); 56 | 57 | if (listOfAtKeys.isEmpty) { 58 | print('The directory does not contains atKeys file(s)'); 59 | return; 60 | } 61 | // Setting recursive to true, to create non-existent path(s) in the output directory 62 | var file = await File('$outputDir$fileName').create(recursive: true); 63 | file.writeAsStringSync('var pkamPrivateKeyMap = '); 64 | Map responseKeyMap = {}; 65 | 66 | await Future.forEach( 67 | listOfAtKeys, 68 | (FileSystemEntity filePath) => 69 | processAtKeysContent(filePath, file, responseKeyMap, preference)); 70 | 71 | file.writeAsStringSync(jsonEncode(responseKeyMap), mode: FileMode.append); 72 | file.writeAsStringSync(';', mode: FileMode.append); 73 | 74 | print('The $fileName is successfully generated in $outputDir'); 75 | } on ArgParserException catch (e) { 76 | print('$e'); 77 | } on Exception catch (e) { 78 | print('Exception : $e'); 79 | } 80 | } 81 | 82 | Future processAtKeysContent(FileSystemEntity fileSystemEntity, File file, 83 | Map decryptedKeysMaps, String preference) async { 84 | // Ignore files that are other than ".atKeys" files. So return. 85 | if (!fileSystemEntity.path.toLowerCase().endsWith('.atkeys')) { 86 | return; 87 | } 88 | 89 | var filePath = fileSystemEntity.path; 90 | var atSign = filePath.substring(filePath.lastIndexOf('/') + 1); 91 | atSign = atSign.replaceFirst('_key.atKeys', ''); 92 | var fileContents = File(filePath).readAsStringSync(); 93 | var keysJSON = json.decode(fileContents); 94 | var aesEncryptionKey = keysJSON['selfEncryptionKey']; 95 | 96 | switch (preference) { 97 | case 'all': 98 | var keyMap = {}; 99 | var aesEncryptionKey = keysJSON['selfEncryptionKey']; 100 | keyMap['pkamPublicKey'] = RSAPublicKey.fromString( 101 | decryptValue(keysJSON['aesPkamPublicKey'], aesEncryptionKey)) 102 | .toString(); 103 | keyMap['pkamPrivateKey'] = RSAPrivateKey.fromString( 104 | decryptValue(keysJSON['aesPkamPrivateKey'], aesEncryptionKey)) 105 | .toString(); 106 | keyMap['encryptionPublicKey'] = RSAPublicKey.fromString( 107 | decryptValue(keysJSON['aesEncryptPublicKey'], aesEncryptionKey)) 108 | .toString(); 109 | keyMap['encryptionPrivateKey'] = RSAPrivateKey.fromString( 110 | decryptValue(keysJSON['aesEncryptPrivateKey'], aesEncryptionKey)) 111 | .toString(); 112 | keyMap['selfEncryptionKey'] = aesEncryptionKey; 113 | decryptedKeysMaps.putIfAbsent(atSign, () => keyMap); 114 | break; 115 | case 'pkam': 116 | var pkamPrivateKey = RSAPrivateKey.fromString( 117 | decryptValue(keysJSON['aesPkamPrivateKey'], aesEncryptionKey)); 118 | decryptedKeysMaps.putIfAbsent(atSign, () => pkamPrivateKey.toString()); 119 | } 120 | } 121 | 122 | ArgParser getArgParser() { 123 | var argParser = ArgParser() 124 | ..addOption('directoryPath', 125 | abbr: 'd', help: 'The directory path which contains atKeys') 126 | ..addOption('outputDir', 127 | abbr: 'o', 128 | help: 'The output directory to place the generated file', 129 | defaultsTo: Directory.current.path) 130 | ..addOption('fileName', 131 | abbr: 'f', 132 | help: 'The filename to write the pkam private key', 133 | defaultsTo: 'at_demo_data.dart') 134 | ..addOption('preference', 135 | abbr: 'p', help: 'Write all keys or only pkam', defaultsTo: 'all'); 136 | return argParser; 137 | } 138 | 139 | String decryptValue(String encryptedValue, String decryptionKey) { 140 | var aesKey = AES(Key.fromBase64(decryptionKey)); 141 | var decrypter = Encrypter(aesKey); 142 | var iv2 = IV(Uint8List(16)); 143 | return decrypter.decrypt64(encryptedValue, iv: iv2); 144 | } 145 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | # The Atsign Foundation Code of Conduct 4 | 5 | Based on 6 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](code_of_conduct.md) 7 | 8 | ## Our Pledge 9 | 10 | We as members, contributors, and leaders pledge to make participation in our 11 | community a harassment-free experience for everyone, regardless of age, body 12 | size, visible or invisible disability, ethnicity, sex characteristics, gender 13 | identity and expression, level of experience, education, socio-economic status, 14 | nationality, personal appearance, race, caste, color, religion, or sexual identity 15 | and orientation. 16 | 17 | We pledge to act and interact in ways that contribute to an open, welcoming, 18 | diverse, inclusive, and healthy community. 19 | 20 | ## Our Standards 21 | 22 | Examples of behavior that contributes to a positive environment for our 23 | community include: 24 | 25 | * Demonstrating empathy and kindness toward other people 26 | * Being respectful of differing opinions, viewpoints, and experiences 27 | * Giving and gracefully accepting constructive feedback 28 | * Accepting responsibility and apologizing to those affected by our mistakes, 29 | and learning from the experience 30 | * Focusing on what is best not just for us as individuals, but for the 31 | overall community 32 | 33 | Examples of unacceptable behavior include: 34 | 35 | * The use of sexualized language or imagery, and sexual attention or 36 | advances of any kind 37 | * Trolling, insulting or derogatory comments, and personal or political attacks 38 | * Public or private harassment 39 | * Publishing others' private information, such as a physical or email 40 | address, without their explicit permission 41 | * Other conduct which could reasonably be considered inappropriate in a 42 | professional setting 43 | 44 | ## Enforcement Responsibilities 45 | 46 | Community leaders are responsible for clarifying and enforcing our standards of 47 | acceptable behavior and will take appropriate and fair corrective action in 48 | response to any behavior that they deem inappropriate, threatening, offensive, 49 | or harmful. 50 | 51 | Community leaders have the right and responsibility to remove, edit, or reject 52 | comments, commits, code, wiki edits, issues, and other contributions that are 53 | not aligned to this Code of Conduct, and will communicate reasons for moderation 54 | decisions when appropriate. 55 | 56 | ## Scope 57 | 58 | This Code of Conduct applies within all community spaces, and also applies when 59 | an individual is officially representing the community in public spaces. 60 | Examples of representing our community include using an official e-mail address, 61 | posting via an official social media account, or acting as an appointed 62 | representative at an online or offline event. 63 | 64 | ## Enforcement 65 | 66 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 67 | reported to the community leaders responsible for enforcement at 68 | conduct@atsign.com. 69 | All complaints will be reviewed and investigated promptly and fairly. 70 | 71 | All community leaders are obligated to respect the privacy and security of the 72 | reporter of any incident. 73 | 74 | ## Enforcement Guidelines 75 | 76 | Community leaders will follow these Community Impact Guidelines in determining 77 | the consequences for any action they deem in violation of this Code of Conduct: 78 | 79 | ### 1. Correction 80 | 81 | **Community Impact**: Use of inappropriate language or other behavior deemed 82 | unprofessional or unwelcome in the community. 83 | 84 | **Consequence**: A private, written warning from community leaders, providing 85 | clarity around the nature of the violation and an explanation of why the 86 | behavior was inappropriate. A public apology may be requested. 87 | 88 | ### 2. Warning 89 | 90 | **Community Impact**: A violation through a single incident or series 91 | of actions. 92 | 93 | **Consequence**: A warning with consequences for continued behavior. No 94 | interaction with the people involved, including unsolicited interaction with 95 | those enforcing the Code of Conduct, for a specified period of time. This 96 | includes avoiding interactions in community spaces as well as external channels 97 | like social media. Violating these terms may lead to a temporary or 98 | permanent ban. 99 | 100 | ### 3. Temporary Ban 101 | 102 | **Community Impact**: A serious violation of community standards, including 103 | sustained inappropriate behavior. 104 | 105 | **Consequence**: A temporary ban from any sort of interaction or public 106 | communication with the community for a specified period of time. No public or 107 | private interaction with the people involved, including unsolicited interaction 108 | with those enforcing the Code of Conduct, is allowed during this period. 109 | Violating these terms may lead to a permanent ban. 110 | 111 | ### 4. Permanent Ban 112 | 113 | **Community Impact**: Demonstrating a pattern of violation of community 114 | standards, including sustained inappropriate behavior, harassment of an 115 | individual, or aggression toward or disparagement of classes of individuals. 116 | 117 | **Consequence**: A permanent ban from any sort of public interaction within 118 | the community. 119 | 120 | ## Attribution 121 | 122 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 123 | version 2.0, available at 124 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 125 | 126 | Community Impact Guidelines were inspired by 127 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 128 | 129 | For answers to common questions about this code of conduct, see the FAQ at 130 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 131 | at [https://www.contributor-covenant.org/translations][translations]. 132 | 133 | [homepage]: https://www.contributor-covenant.org 134 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 135 | [Mozilla CoC]: https://github.com/mozilla/diversity 136 | [FAQ]: https://www.contributor-covenant.org/faq 137 | [translations]: https://www.contributor-covenant.org/translations 138 | -------------------------------------------------------------------------------- /packages/at_repl/lib/features/inspect_notifications.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:convert'; 3 | import 'package:io/ansi.dart'; 4 | import '../interactive_session.dart'; 5 | 6 | class InspectNotificationsSession implements InteractiveSession { 7 | final List> _notifications; 8 | final IOSink _outputStream; 9 | final Future Function(String) _executeCommand; 10 | bool _isActive = true; 11 | bool _waitingForAction = false; 12 | int _selectedNotificationIndex = -1; 13 | 14 | InspectNotificationsSession(this._notifications, this._outputStream, this._executeCommand) { 15 | _showNotificationList(); 16 | _outputStream.writeln(cyan.wrap("\nEntering interactive mode. Type a number to select a notification, 'l' to relist, or 'q' to quit.")); 17 | } 18 | 19 | @override 20 | bool handleInput(String input) { 21 | if (!_isActive) return false; 22 | 23 | input = input.trim(); 24 | 25 | if (input.toLowerCase() == 'q' || input.toLowerCase() == 'quit') { 26 | _outputStream.writeln(green.wrap("Exiting interactive mode")); 27 | exit(); 28 | return false; 29 | } 30 | 31 | if (_waitingForAction) { 32 | return _handleActionInput(input); 33 | } 34 | 35 | if (input.toLowerCase() == 'l' || input.toLowerCase() == 'list') { 36 | _showNotificationList(); 37 | return true; 38 | } 39 | 40 | final index = int.tryParse(input); 41 | if (index == null || index < 1 || index > _notifications.length) { 42 | _outputStream.writeln(red.wrap("Invalid selection. Enter a number 1-${_notifications.length}, 'l' to relist, or 'q' to quit.")); 43 | return true; 44 | } 45 | 46 | final selectedNotification = _notifications[index - 1]; 47 | final notificationId = selectedNotification['id']; 48 | _outputStream.writeln(green.wrap("Selected notification ID: $notificationId")); 49 | _outputStream.writeln(cyan.wrap("Enter 'v' to view details or 'd' to delete:")); 50 | 51 | _waitingForAction = true; 52 | _selectedNotificationIndex = index - 1; 53 | 54 | return true; 55 | } 56 | 57 | bool _handleActionInput(String input) { 58 | final action = input.trim().toLowerCase(); 59 | final selectedNotification = _notifications[_selectedNotificationIndex]; 60 | 61 | if (action == 'v') { 62 | _handleViewNotification(selectedNotification); 63 | } else if (action == 'd') { 64 | _handleDeleteNotification(selectedNotification, _selectedNotificationIndex); 65 | } else { 66 | _outputStream.writeln(red.wrap("Invalid action. Enter 'v' to view or 'd' to delete.")); 67 | return true; 68 | } 69 | 70 | _waitingForAction = false; 71 | _selectedNotificationIndex = -1; 72 | 73 | if (_notifications.isNotEmpty) { 74 | _outputStream.writeln(cyan.wrap("Type a number to select another notification, 'l' to relist, or 'q' to quit.")); 75 | } 76 | 77 | return true; 78 | } 79 | 80 | void _handleViewNotification(Map notification) { 81 | _outputStream.writeln(green.wrap("Notification details:")); 82 | const JsonEncoder encoder = JsonEncoder.withIndent(' '); 83 | _outputStream.writeln(encoder.convert(notification)); 84 | } 85 | 86 | void _handleDeleteNotification(Map notification, int index) async { 87 | final notificationId = notification['id']; 88 | try { 89 | await _executeCommand("notify:remove:$notificationId\n"); 90 | _outputStream.writeln(green.wrap("Successfully deleted notification: $notificationId")); 91 | _notifications.removeAt(index); 92 | 93 | if (_notifications.isEmpty) { 94 | _outputStream.writeln(lightYellow.wrap("No more notifications. Exiting interactive mode.")); 95 | exit(); 96 | return; 97 | } 98 | 99 | _showNotificationList(); 100 | } catch (e) { 101 | _outputStream.writeln(red.wrap("Error deleting notification: $e")); 102 | } 103 | } 104 | 105 | void _showNotificationList() { 106 | _outputStream.writeln(green.wrap("\nFound ${_notifications.length} notification(s):")); 107 | _showNotificationTable(_notifications, _outputStream); 108 | } 109 | 110 | void _showNotificationTable(List> notifications, IOSink outputStream) { 111 | outputStream.writeln("${'#'.padRight(5)} | ${'ID'.padRight(36)} | ${'From'.padRight(15)} | ${'To'.padRight(15)} | Timestamp"); 112 | outputStream.writeln("${'─' * 5}─┼─${'─' * 36}─┼─${'─' * 15}─┼─${'─' * 15}─┼─${'─' * 20}"); 113 | 114 | for (int i = 0; i < notifications.length; i++) { 115 | final indexStr = (i + 1).toString().padRight(5); 116 | final notification = notifications[i]; 117 | final id = (notification['id'] ?? 'Unknown').toString().padRight(36); 118 | final from = (notification['from'] ?? 'Unknown').toString().padRight(15); 119 | final to = (notification['to'] ?? 'Unknown').toString().padRight(15); 120 | 121 | // Convert epochMillis to readable timestamp 122 | String timestamp = 'Unknown'; 123 | if (notification['epochMillis'] != null) { 124 | try { 125 | final epochMillis = notification['epochMillis'] as int; 126 | final dateTime = DateTime.fromMillisecondsSinceEpoch(epochMillis); 127 | timestamp = '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}'; 128 | } catch (e) { 129 | timestamp = 'Invalid'; 130 | } 131 | } 132 | 133 | outputStream.writeln("$indexStr | $id | $from | $to | $timestamp"); 134 | } 135 | 136 | outputStream.writeln(cyan.wrap("\nType a number to select a notification, 'l' to relist, or 'q' to quit.")); 137 | } 138 | 139 | @override 140 | String getPrompt() { 141 | if (_waitingForAction) { 142 | return "action> "; 143 | } 144 | return "inspect_notifications> "; 145 | } 146 | 147 | @override 148 | bool get isActive => _isActive; 149 | 150 | @override 151 | void exit() { 152 | _isActive = false; 153 | _waitingForAction = false; 154 | _selectedNotificationIndex = -1; 155 | } 156 | } 157 | 158 | List> parseNotifications(String response) { 159 | try { 160 | // The response should be a JSON array 161 | final decoded = jsonDecode(response.trim()); 162 | if (decoded is List) { 163 | return decoded.cast>(); 164 | } else { 165 | return []; 166 | } 167 | } catch (e) { 168 | // Try parsing line by line as fallback 169 | final notifications = >[]; 170 | final lines = response.split('\n'); 171 | 172 | for (final line in lines) { 173 | if (line.trim().isEmpty || line.startsWith('data:')) continue; 174 | 175 | try { 176 | final decoded = jsonDecode(line); 177 | if (decoded is Map) { 178 | notifications.add(decoded); 179 | } 180 | } catch (e) { 181 | // Skip lines that can't be parsed as JSON 182 | } 183 | } 184 | 185 | return notifications; 186 | } 187 | } 188 | 189 | // All interactive functionality has been moved to InspectNotificationsSession -------------------------------------------------------------------------------- /packages/at_cli/lib/src/at_cli.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | import 'package:args/args.dart'; 5 | import 'package:at_cli/src/command_line_parser.dart'; 6 | import 'package:at_client/at_client.dart'; 7 | import 'package:at_cli/src/preference.dart'; 8 | import 'package:encrypt/encrypt.dart'; 9 | import 'package:at_commons/at_builders.dart'; 10 | 11 | /// A class to execute verbs from commandline. 12 | class AtCli { 13 | static final AtCli _singleton = AtCli._internal(); 14 | 15 | AtCli._internal(); 16 | 17 | factory AtCli.getInstance() { 18 | return _singleton; 19 | } 20 | 21 | late String _atSign; 22 | 23 | late String _aesEncryptionKey; 24 | late String _pkamPrivateKey; 25 | 26 | AtClient? _atClientImpl; 27 | 28 | /// Method to create AtLookup instance with the preferences 29 | Future init( 30 | String currentAtSign, AtCliPreference atCliPreference) async { 31 | _atSign = currentAtSign; 32 | 33 | var keysJSON = await getSecretFromAtKeys(atCliPreference.authKeyFile); 34 | _aesEncryptionKey = keysJSON['selfEncryptionKey'].toString().trim(); 35 | _pkamPrivateKey = 36 | decryptValue(keysJSON['aesPkamPrivateKey'], _aesEncryptionKey).trim(); 37 | 38 | var atClientPreference = 39 | _getAtClientPreference(_pkamPrivateKey, atCliPreference); 40 | 41 | AtClientManager atClientManager = AtClientManager.getInstance(); 42 | await atClientManager.setCurrentAtSign( 43 | _atSign, atCliPreference.namespace, atClientPreference); 44 | 45 | _atClientImpl = atClientManager.atClient; 46 | if (_atClientImpl == null) { 47 | throw Exception('unable to create at client instance'); 48 | } 49 | } 50 | 51 | /// Method to execute verb 52 | /// input - Commandline arguments and values 53 | /// return value - verb result 54 | Future execute( 55 | AtCliPreference atCliPreference, ArgResults arguments) async { 56 | var verb = arguments['verb']; 57 | bool auth = arguments['auth']; 58 | dynamic result; 59 | try { 60 | switch (verb) { 61 | case 'update': 62 | var builder = UpdateVerbBuilder(); 63 | if (arguments['public']) { 64 | builder.isPublic = true; 65 | } 66 | builder.atKey = arguments['key']; 67 | builder.sharedBy = _atSign; 68 | builder.sharedWith = arguments['shared_with']; 69 | builder.value = arguments['value']; 70 | if (!builder.checkParams()) { 71 | throw Exception( 72 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 73 | } 74 | var command = builder.buildCommand(); 75 | result = await _atClientImpl! 76 | .getRemoteSecondary()! 77 | .executeCommand(command, auth: true); 78 | break; 79 | case 'llookup': 80 | var builder = LLookupVerbBuilder(); 81 | builder.atKey = arguments['key']; 82 | builder.sharedBy = _atSign; 83 | builder.sharedWith = arguments['shared_with']; 84 | builder.isPublic = arguments['public']; 85 | builder.sharedBy = (arguments['shared_by'] != null) 86 | ? arguments['shared_by'] 87 | : _atSign; 88 | if (!builder.checkParams()) { 89 | throw Exception( 90 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 91 | } 92 | var command = builder.buildCommand(); 93 | result = await _atClientImpl! 94 | .getRemoteSecondary()! 95 | .executeCommand(command, auth: true); 96 | break; 97 | case 'lookup': 98 | var builder = LookupVerbBuilder(); 99 | builder.atKey = arguments['key']; 100 | builder.sharedBy = (arguments['shared_by'] != null) 101 | ? arguments['shared_by'] 102 | : _atSign; 103 | if (!builder.checkParams()) { 104 | throw Exception( 105 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 106 | } 107 | var command = builder.buildCommand(); 108 | result = await _atClientImpl! 109 | .getRemoteSecondary()! 110 | .executeCommand(command, auth: true); 111 | break; 112 | case 'plookup': 113 | var builder = PLookupVerbBuilder(); 114 | builder.atKey = arguments['key']; 115 | builder.sharedBy = (arguments['shared_by'] != null) 116 | ? arguments['shared_by'] 117 | : _atSign; 118 | if (!builder.checkParams()) { 119 | throw Exception( 120 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 121 | } 122 | var command = builder.buildCommand(); 123 | result = await _atClientImpl! 124 | .getRemoteSecondary()! 125 | .executeCommand(command, auth: true); 126 | break; 127 | case 'delete': 128 | var builder = DeleteVerbBuilder(); 129 | builder.atKey = arguments['key']; 130 | builder.sharedWith = arguments['shared_with']; 131 | builder.isPublic = arguments['public']; 132 | builder.sharedBy = (arguments['shared_by'] != null) 133 | ? arguments['shared_by'] 134 | : _atSign; 135 | if (!builder.checkParams()) { 136 | throw Exception( 137 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 138 | } 139 | var command = builder.buildCommand(); 140 | result = await _atClientImpl! 141 | .getRemoteSecondary()! 142 | .executeCommand(command, auth: true); 143 | break; 144 | case 'scan': 145 | var builder = ScanVerbBuilder(); 146 | builder.regex = arguments['regex']; 147 | builder.sharedBy = arguments['shared_by']; 148 | var command = builder.buildCommand(); 149 | if (!builder.checkParams()) { 150 | throw Exception( 151 | 'Invalid command \n ${CommandLineParser.getUsage()}'); 152 | } 153 | result = await _atClientImpl! 154 | .getRemoteSecondary()! 155 | .executeCommand(command, auth: auth); 156 | break; 157 | } 158 | return result; 159 | } on Exception { 160 | rethrow; 161 | } 162 | } 163 | 164 | /// Method to execute command 165 | /// input - command and isAuth 166 | /// return value - command response 167 | Future executeCommand( 168 | String currentAtSign, AtCliPreference atCliPreference, String command, 169 | {bool isAuth = false}) async { 170 | dynamic result; 171 | try { 172 | command = command + '\n'; 173 | if (isAuth) { 174 | result = await _atClientImpl! 175 | .getRemoteSecondary()! 176 | .executeCommand(command, auth: true); 177 | } else { 178 | result = 179 | await _atClientImpl!.getRemoteSecondary()!.executeCommand(command); 180 | } 181 | } on Exception { 182 | rethrow; 183 | } 184 | return result; 185 | } 186 | 187 | String getAuthKey(String atSign, String path) { 188 | String contents; 189 | try { 190 | contents = File(path).readAsStringSync(); 191 | } catch (error) { 192 | rethrow; 193 | } 194 | return contents; 195 | } 196 | 197 | ///Method to get pkam key and encryption key from atKeys file 198 | Future getSecretFromAtKeys(String filePath) async { 199 | try { 200 | var fileContents = File(filePath).readAsStringSync(); 201 | return json.decode(fileContents); 202 | } on Exception catch (e) { 203 | throw Exception('Exception while reading atKeys file : $e'); 204 | } 205 | } 206 | 207 | String decryptValue(String encryptedValue, String decryptionKey) { 208 | var aesKey = AES(Key.fromBase64(decryptionKey)); 209 | var decrypter = Encrypter(aesKey); 210 | var iv2 = IV(Uint8List(16)); 211 | return decrypter.decrypt64(encryptedValue, iv: iv2); 212 | } 213 | 214 | AtClientPreference _getAtClientPreference( 215 | String privateKey, AtCliPreference atCliPreference) { 216 | var preference = AtClientPreference(); 217 | preference.isLocalStoreRequired = false; 218 | preference.privateKey = preference.rootDomain = atCliPreference.rootDomain; 219 | preference.outboundConnectionTimeout = 60000; 220 | preference.privateKey = privateKey; 221 | return preference; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /packages/at_repl/lib/repl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'package:at_client/at_client.dart'; 5 | import 'package:at_onboarding_cli/at_onboarding_cli.dart'; 6 | import 'package:at_repl/repl_exception.dart'; 7 | import 'package:io/ansi.dart'; 8 | import 'interactive_session.dart'; 9 | import 'repl_mode.dart'; 10 | import 'features/help.dart'; 11 | import 'features/get.dart'; 12 | import 'features/put.dart'; 13 | import 'features/delete.dart'; 14 | import 'features/scan.dart'; 15 | import 'features/inspect_keys.dart'; 16 | import 'features/inspect_notifications.dart'; 17 | import 'features/monitor.dart'; 18 | import 'constants.dart'; 19 | 20 | class REPL { 21 | late Stream inputStream; 22 | late IOSink outputStream; 23 | late AtClient atClient; 24 | InteractiveSession? currentSession; 25 | ReplMode currentMode = ReplMode.main; 26 | 27 | REPL({ 28 | Stream? inputStream, 29 | IOSink? outputStream, 30 | }) { 31 | this.inputStream = inputStream ?? stdin.transform(utf8.decoder).transform(const LineSplitter()); 32 | this.outputStream = outputStream ?? stdout; 33 | } 34 | 35 | Future authenticate({required String rootDomain, required int rootPort, required String atSign, String? keysFile}) async { 36 | return await _pkamAuth(rootDomain, rootPort, atSign, keysFile); 37 | } 38 | 39 | void start() { 40 | outputStream.writeln("${green.wrap("at_repl started") ?? "at_repl started"}. ${cyan.wrap("Type /help for available commands or /quit to quit.") ?? "Type /help for available commands or /quit to quit."}"); 41 | _showPrompt(); 42 | 43 | inputStream.listen((String input) { 44 | input = input.trim(); 45 | 46 | if (input.isEmpty) { 47 | _showPrompt(); 48 | return; 49 | } 50 | 51 | // Check if we're in interactive mode 52 | if (currentSession != null && currentSession!.isActive) { 53 | final continueSession = currentSession!.handleInput(input); 54 | if (!continueSession || !currentSession!.isActive) { 55 | currentSession = null; 56 | currentMode = ReplMode.main; 57 | } 58 | _showPrompt(); 59 | return; 60 | } 61 | 62 | if (input.startsWith('/')) { 63 | _handleCommand(input); 64 | } else { 65 | _handleRawProtocolCommand(input); 66 | } 67 | 68 | _showPrompt(); 69 | }); 70 | } 71 | 72 | void _showPrompt() { 73 | final atSign = _getAtSign(); 74 | if (currentSession != null && currentSession!.isActive) { 75 | outputStream.write("$atSign ${currentSession!.getPrompt()}"); 76 | } else { 77 | outputStream.write("$atSign: "); 78 | } 79 | 80 | if (outputStream == stdout) { 81 | stdout.flush(); 82 | } 83 | } 84 | 85 | String _getAtSign() { 86 | try { 87 | return atClient.getCurrentAtSign() ?? "@unknown"; 88 | } catch (e) { 89 | return "@unknown"; 90 | } 91 | } 92 | 93 | Future _pkamAuth(final String rootDomain, final int rootPort, final String atSign, final String? keysFile) async { 94 | AtOnboardingPreference pref = AtOnboardingPreference() 95 | ..namespace = 'at_repl' 96 | ..rootDomain = rootDomain 97 | ..rootPort = rootPort; 98 | 99 | if (keysFile != null) { 100 | pref.atKeysFilePath = keysFile; 101 | } 102 | 103 | AtOnboardingService service = AtOnboardingServiceImpl(atSign, pref); 104 | bool success = await service.authenticate(); 105 | if(success) { 106 | atClient = service.atClient!; 107 | return true; 108 | } 109 | return success; 110 | } 111 | 112 | void _handleRawProtocolCommand(String input) { 113 | if (!input.endsWith('\n')) { 114 | input += '\n'; 115 | } 116 | outputStream.writeln("Executing raw command: ${input.trim()}"); 117 | _executeCommand(input).then((response) { 118 | outputStream.writeln("Response: $response"); 119 | }).catchError((error) { 120 | outputStream.writeln(red.wrap("Error executing command: $error")); 121 | }); 122 | } 123 | 124 | /// This function is for executing protocol verbs 125 | /// Make sure that you add a \n at the end of the command so that you can get a return string back 126 | Future _executeCommand(String command) async { 127 | final RemoteSecondary rs = atClient.getRemoteSecondary()!; 128 | final String? response = (await rs.executeCommand(command, auth: true)); 129 | if (response == null) { 130 | throw REPLException( 131 | 'Result is null for some reason after executing command: $command'); 132 | } 133 | return response; 134 | } 135 | 136 | void _handleCommand(String input) { 137 | try { 138 | if (input == '/q' || input == '/quit') { 139 | outputStream.writeln(green.wrap("Goodbye!")); 140 | exit(0); 141 | } else if (input == '/help') { 142 | printUsage(outputStream); 143 | } else if (input.startsWith('/get ')) { 144 | handleGet(input, atClient, outputStream); 145 | } else if (input.startsWith('/put ')) { 146 | handlePut(input, atClient, outputStream); 147 | } else if (input.startsWith('/delete ')) { 148 | handleDelete(input, atClient, outputStream); 149 | } else if (input.startsWith('/scan')) { 150 | handleScan(input, atClient, outputStream); 151 | } else if (input.startsWith('/inspect_notify')) { 152 | _handleInspectNotifications(input); 153 | } else if (input.startsWith('/inspect')) { 154 | _handleInspectKeys(input); 155 | } else if (input.startsWith('/monitor')) { 156 | _handleMonitor(input); 157 | } else { 158 | outputStream.writeln(red.wrap("Unknown command: $input")); 159 | } 160 | } catch (e) { 161 | outputStream.writeln(red.wrap("Error: $e")); 162 | } 163 | } 164 | 165 | void _handleInspectKeys(String input) async { 166 | final parts = input.split(' '); 167 | String? userRegex = parts.length > 1 ? parts.sublist(1).join(' ') : null; 168 | if (userRegex != null) { 169 | userRegex = userRegex.trim(); 170 | if (userRegex.isEmpty) { 171 | userRegex = null; 172 | } 173 | } 174 | String actualRegex = userRegex ?? defaultInspectRegex; 175 | try { 176 | outputStream.writeln(cyan.wrap("Inspecting keys with regex: '$actualRegex' ...")); 177 | final totalKeys = await getAtKeys(atClient, regex: '.*', showHiddenKeys: true); 178 | final keys = await getAtKeys(atClient, regex: actualRegex, showHiddenKeys: true); 179 | if (keys.isEmpty) { 180 | outputStream.writeln(lightYellow.wrap("No keys found (0 of ${totalKeys.length} total keys)")); 181 | return; 182 | } 183 | outputStream.writeln(green.wrap("\nShowing ${keys.length} of ${totalKeys.length} key(s):")); 184 | currentSession = InspectKeysSession(keys, atClient, outputStream); 185 | currentMode = ReplMode.inspectKeys; 186 | } catch (e) { 187 | outputStream.writeln(red.wrap("Error inspecting keys: $e")); 188 | } 189 | } 190 | 191 | void _handleInspectNotifications(String input) async { 192 | try { 193 | outputStream.writeln(cyan.wrap("Fetching notifications...")); 194 | 195 | final response = await _executeCommand("notify:list\n"); 196 | // remove data: prefix if present 197 | final cleanedResponse = response.replaceAll(RegExp(r'^data:\s*'), ''); 198 | if (cleanedResponse.isEmpty || cleanedResponse == '[]') { 199 | outputStream.writeln(lightYellow.wrap("No notifications found")); 200 | return; 201 | } 202 | final notifications = parseNotifications(cleanedResponse); 203 | 204 | if (notifications.isEmpty) { 205 | outputStream.writeln(lightYellow.wrap("No notifications found")); 206 | return; 207 | } 208 | 209 | outputStream.writeln(green.wrap("\nFound ${notifications.length} notification(s):")); 210 | 211 | currentSession = InspectNotificationsSession(notifications, outputStream, _executeCommand); 212 | currentMode = ReplMode.inspectNotifications; 213 | 214 | } catch (e) { 215 | outputStream.writeln(red.wrap("Error inspecting notifications: $e")); 216 | } 217 | } 218 | 219 | void _handleMonitor(String input) { 220 | final parts = input.split(' '); 221 | String? regex = parts.length > 1 ? parts.sublist(1).join(' ') : null; 222 | 223 | if (regex == null || regex.isEmpty) { 224 | regex = defaultMonitorRegex; 225 | } 226 | 227 | try { 228 | currentSession = MonitorSession(atClient, regex: regex, output: outputStream); 229 | currentMode = ReplMode.monitor; 230 | 231 | } catch (e) { 232 | outputStream.writeln(red.wrap("Error starting monitor: $e")); 233 | } 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /packages/at_pkam/README.md: -------------------------------------------------------------------------------- 1 | The Atsign FoundationThe Atsign Foundation 2 | 3 | ## at_pkam 4 | 5 | A command line tool to create PKAM authentication tokens. 6 | 7 | ### Building 8 | 9 | __Assumption__ - you have the [Dart SDK](https://dart.dev/get-dart) installed. 10 | The version should be >= 2.12.0 and <4.0.0. 11 | 12 | First fetch dependencies (as defined in pubspec.yaml): 13 | 14 | ```bash 15 | dart pub get 16 | ``` 17 | 18 | It's now possible to run the command in the Dart VM: 19 | 20 | ```bash 21 | dart run bin/main.dart 22 | ``` 23 | 24 | At which point it will print out some usage instructions: 25 | 26 | ``` 27 | FormatException: ArgParserException 28 | -p, --file_path .keys or .zip file path which contains keys 29 | -r, --from_response from:@atSign response 30 | -a, --aes_key aes key file path 31 | (defaults to "") 32 | ``` 33 | 34 | Starting up the VM every time for a small command line app is a little slow, 35 | so it's better to create a compiled binary: 36 | 37 | ```bash 38 | dart compile exe bin/main.dart -o pkam 39 | ``` 40 | 41 | The binary can now be run with `./pkam` along with appropriate arguments. 42 | 43 | ### Usage 44 | 45 | The PKAM tool is used to generate an authentication token to sign into an 46 | @platform secondary server. You're going to need: 47 | 48 | * The name of the secondary e.g. @turingcomplete 49 | * The address and port of the secondary e.g. turingcomplete.mydomain.com:6565 50 | * Keys for the secondary generated during activation e.g. @turingcomplete_key.atKeys 51 | 52 | #### Connect to the secondary with OpenSSL 53 | 54 | __Assumption__ - you have [OpenSSL](https://www.openssl.org/) installed. 55 | 56 | ```bash 57 | openssl s_client -ign_eof turingcomplete.mydomain.com:6565 58 | ``` 59 | 60 | There will then follow a bunch of certificate and handshake information like: 61 | 62 | ``` 63 | CONNECTED(00000003) 64 | depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1 65 | verify return:1 66 | depth=1 C = US, O = Let's Encrypt, CN = R3 67 | verify return:1 68 | depth=0 CN = turingcomplete.mydomain.com 69 | verify return:1 70 | --- 71 | Certificate chain 72 | 0 s:CN = turingcomplete.mydomain.com 73 | i:C = US, O = Let's Encrypt, CN = R3 74 | 1 s:C = US, O = Let's Encrypt, CN = R3 75 | i:C = US, O = Internet Security Research Group, CN = ISRG Root X1 76 | 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1 77 | i:O = Digital Signature Trust Co., CN = DST Root CA X3 78 | --- 79 | Server certificate 80 | -----BEGIN CERTIFICATE----- 81 | MIIFKDCCBBCgAwIBAgISAwjRI5/zjWcPXyhPKz1sLdrKMA0GCSqGSIb3DQEBCwUA 82 | MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD 83 | EwJSMzAeFw0yMTA2MDExMTMyMTBaFw0yMTA4MzAxMTMyMTBaMBwxGjAYBgNVBAMT 84 | EWRlc3MuamF2YWdvbmUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 85 | AQEAxHfYduxaBmQbS/SvmJY1rUoyzG5sr0H7M95sSwGyMcnpULEGq54g5+fSumCI 86 | 57OlC12T2skkq+Bz3I6HPppTHFLyJ1vzxZmlPRJhemc+lttnaDVI2Jnx/1IIlN3k 87 | D8VOCJq0uGHX0NVjERdAhYaroAlsAC4fulHHiArO0uq0KPOjmaHvhZU2gfv0g8w2 88 | uRiebwg0BQSkO5nY+8CEvnPwoW10dkP2EERZ3sk4iJscJWxK1u5qaZfUXu2aA23I 89 | s5u2MNKxCEPz97EBC0qAcJcgtaS9TBJhS0MFD3NhmmR+NHNJ3OollOfDOVNxZRyi 90 | fbQWnmB4/Xc6w1qwcSxOlQz8CQIDAQABo4ICTDCCAkgwDgYDVR0PAQH/BAQDAgWg 91 | MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G 92 | A1UdDgQWBBQAr+DwphsaqG0vRD9MsL38VqU3JTAfBgNVHSMEGDAWgBQULrMXt1hW 93 | y65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6 94 | Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iu 95 | b3JnLzAcBgNVHREEFTATghFkZXNzLmphdmFnb25lLm9yZzBMBgNVHSAERTBDMAgG 96 | BmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz 97 | LmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AG9Tdqwx 98 | 8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABecePV2MAAAQDAEcwRQIgf1kf 99 | G7TYMivat36rY10ofYf6VURYwJMTENKSQWKYK6ICIQDhBDBbVWzhK9NiKloTWWpi 100 | ABV3FJqgUqAI+IN9zQBSdAB2APZclC/RdzAiFFQYCDCUVo7jTRMZM7/fDC8gC8xO 101 | 8WTjAAABecePVzQAAAQDAEcwRQIgfPT+NMBjqeCEXpWuhAm/JBfEP3zJEqn1ha7Z 102 | /6YOrG4CIQDYcgMZYN3BPS1G97sa5bKyeIk7Ph7ZXGVWLLd3+rS0UTANBgkqhkiG 103 | 9w0BAQsFAAOCAQEALPibZueYDjXzbZQXYbCnYeZJU+u6jjEOgJOP/9GyN5BM96o/ 104 | 2U4cg5aOSAFrG3T3bHzSvcWws68bqwvAd1Z3+6ZMzERd/ecLCQ3BWsio5kDlbxmk 105 | eFN3C1HUDAeXahpOaAAXPtWTrNZtE5fQMM5ZY7dOZSArnzUPaHM3BYy8zzKKu4fD 106 | N6UjKBOEvc3XwkP739FjEdHfaHckaWwQGuPSYcTO6vtQ307+it1tWV1+9vn0uswx 107 | 1BN7WSzRQoQcPGmrcpO3vgt4O+5CXk0xTtyabMGeTgGmpadIotnpvbld+W4N+p4P 108 | xJQgdx1Vh8aTNBDKsJTmj3a6rckwdtHUBpj6qg== 109 | -----END CERTIFICATE----- 110 | subject=CN = turingcomplete.mydomain.com 111 | 112 | issuer=C = US, O = Let's Encrypt, CN = R3 113 | 114 | --- 115 | No client certificate CA names sent 116 | Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512:RSA+SHA1 117 | Shared Requested Signature Algorithms: ECDSA+SHA256:RSA-PSS+SHA256:RSA+SHA256:ECDSA+SHA384:RSA-PSS+SHA384:RSA+SHA384:RSA-PSS+SHA512:RSA+SHA512 118 | Peer signing digest: SHA256 119 | Peer signature type: RSA-PSS 120 | Server Temp Key: X25519, 253 bits 121 | --- 122 | SSL handshake has read 4541 bytes and written 419 bytes 123 | Verification: OK 124 | --- 125 | New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 126 | Server public key is 2048 bit 127 | Secure Renegotiation IS NOT supported 128 | Compression: NONE 129 | Expansion: NONE 130 | No ALPN negotiated 131 | Early data was not sent 132 | Verify return code: 0 (ok) 133 | --- 134 | --- 135 | Post-Handshake New Session Ticket arrived: 136 | SSL-Session: 137 | Protocol : TLSv1.3 138 | Cipher : TLS_AES_256_GCM_SHA384 139 | Session-ID: 620EB882C045BA493B48D36D13CDE0CD65C83EBF0ACA471566C0EB197383AA5C 140 | Session-ID-ctx: 141 | Resumption PSK: 73465DE8E3702D9633D80008BC5B62B8819CB9CFA1A0E7C1EA9DCA9709096F3162A83AD3BB0699DBF2ECC71787659600 142 | PSK identity: None 143 | PSK identity hint: None 144 | SRP username: None 145 | TLS session ticket lifetime hint: 172800 (seconds) 146 | TLS session ticket: 147 | 0000 - de 6a 37 8d 80 10 d5 ba-53 b5 68 78 3f 6d 39 c5 .j7.....S.hx?m9. 148 | 0010 - 9c e7 3f 54 e4 0d bb d7-3f 2a bf a3 4f 4c 40 09 ..?T....?*..OL@. 149 | 0020 - 44 77 83 c6 97 8d 68 eb-7c 19 e9 6d a7 58 aa 1a Dw....h.|..m.X.. 150 | 0030 - 06 af 8d 02 bf c5 88 e1-9d a8 28 94 cb 92 e1 6d ..........(....m 151 | 0040 - f9 01 78 e1 dd b9 f5 0e-0b 0c 26 d8 0e 47 21 f5 ..x.......&..G!. 152 | 0050 - c7 9e d6 b6 b9 7a 64 a8-96 44 7f 81 e2 a8 75 04 .....zd..D....u. 153 | 0060 - e9 65 8f 20 da 1e 4e a4-0d b4 4f dd eb 2e a8 68 .e. ..N...O....h 154 | 0070 - 84 30 b3 17 23 a7 51 c2-c4 7d 07 2e 0f c4 0a 23 .0..#.Q..}.....# 155 | 0080 - 4e 97 e1 6e 69 1e c7 57-c2 e5 0d 8f 69 35 09 a6 N..ni..W....i5.. 156 | 0090 - 79 ee 75 4f 09 43 a2 5e-17 8c 23 b4 8e 9e bc 7c y.uO.C.^..#....| 157 | 00a0 - f7 ca 6b 79 e1 3d 17 d2-3d 08 f4 68 4d 78 dc 68 ..ky.=..=..hMx.h 158 | 159 | Start Time: 1622826331 160 | Timeout : 7200 (sec) 161 | Verify return code: 0 (ok) 162 | Extended master secret: no 163 | Max Early Data: 0 164 | --- 165 | read R BLOCK 166 | --- 167 | Post-Handshake New Session Ticket arrived: 168 | SSL-Session: 169 | Protocol : TLSv1.3 170 | Cipher : TLS_AES_256_GCM_SHA384 171 | Session-ID: 57334F5145A174B8F8E1252068F4B42DFEAB6F823E1B6E4B507B41DEB97AC070 172 | Session-ID-ctx: 173 | Resumption PSK: D52439A67A5398787A089F8DFFB7D44EF469E7DD989E5ADA2EC78DFE80B0FFFB1B7CB0EB87564E716ECC9F6D826E377F 174 | PSK identity: None 175 | PSK identity hint: None 176 | SRP username: None 177 | TLS session ticket lifetime hint: 172800 (seconds) 178 | TLS session ticket: 179 | 0000 - de 6a 37 8d 80 10 d5 ba-53 b5 68 78 3f 6d 39 c5 .j7.....S.hx?m9. 180 | 0010 - 4f 0a 49 1f b5 f1 f0 d9-65 66 af 84 63 ee b6 63 O.I.....ef..c..c 181 | 0020 - 59 63 26 5a 66 f7 18 c1-2d f4 b3 21 46 60 22 9d Yc&Zf...-..!F`". 182 | 0030 - 75 b4 84 dd 87 40 6d bd-34 36 e1 52 12 50 c8 07 u....@m.46.R.P.. 183 | 0040 - 26 49 9a 9b 9d bc c6 5d-88 e5 99 56 ae 2f d8 d6 &I.....]...V./.. 184 | 0050 - 52 9f 22 a7 0d 50 8c cc-a9 90 1e bc 4e 6d 04 83 R."..P......Nm.. 185 | 0060 - 15 13 37 4c 9c 1f 8c ce-05 e0 93 7e bf 92 98 e3 ..7L.......~.... 186 | 0070 - d5 f5 52 71 ae ee 53 29-1c a0 62 29 c7 b7 a4 d9 ..Rq..S)..b).... 187 | 0080 - 30 71 0b e1 8d 67 b1 9d-de 7d 9d 58 a8 db c3 31 0q...g...}.X...1 188 | 0090 - 2f dc db ba 2b b1 79 47-67 af e8 13 d2 c1 ab 34 /...+.yGg......4 189 | 00a0 - d3 55 7a 3e 5d 1f eb 34-b3 27 24 26 b5 8a 69 ec .Uz>]..4.'$&..i. 190 | 191 | Start Time: 1622826331 192 | Timeout : 7200 (sec) 193 | Verify return code: 0 (ok) 194 | Extended master secret: no 195 | Max Early Data: 0 196 | --- 197 | read R BLOCK 198 | ``` 199 | 200 | Culminating in an `@` prompt. At the prompt initiate the authentication process: 201 | 202 | ``` 203 | from:@turingcomplete 204 | ``` 205 | 206 | This will result in a response like: 207 | 208 | ``` 209 | data:_82a2a0e5-9ad4-4f71-99f7-0cdd3798c7d0@turingcomplete:037e245d-34a6-4470-8764-992419a6c2fe 210 | ``` 211 | 212 | Copy the response after `data:` and use it in the PKAM tool (__Assumption__ you 213 | have two sessions running, one for OpenSSL and the other for PKAM): 214 | 215 | ``` 216 | ./pkam -p ~/@turingcomplete_key.atKeys -r \ 217 | _82a2a0e5-9ad4-4f71-99f7-0cdd3798c7d0@turingcomplete:037e245d-34a6-4470-8764-992419a6c2fe 218 | ``` 219 | 220 | __Assumption__ - your keys file has been copied to the HOME directory. 221 | 222 | PKAM will generate a response like: 223 | 224 | ``` 225 | klW5RKSA6IevZJhR4Ig0rqqxbGt1f5G9Oa8yw7y/+AMTlu6LN8KUGvPTqc0EP9T0zUjxCVLBqvvD2e0ugRuarigEmQl0vktb7d7Vp3Bve6J+EI8rKhTDEzhe1XfN0LJTov1Gpo6DzYHq8bnP/4APNxnKPXS+ls5aIXh/I8yvxlO90+CHxyjjsMRm2c33eD+0vwGuGq+X+wT/YOU4TzAllIKlxHlA5kehTgS5WGinl3A8WG5pKE/+gFu3SRg/5sFwKf2E8DyYQaGRMuyMbOfLg4c3NTODyyMxZWx9bc2gtJ/kC52ngsPyBS3X0eyt1BSZliX/NeqTVTEWzMZMiOcf9w== 226 | ``` 227 | 228 | Go back to the openssl session and prefix `pkam:` to the response: 229 | 230 | ``` 231 | pkam:klW5RKSA6IevZJhR4Ig0rqqxbGt1f5G9Oa8yw7y/+AMTlu6LN8KUGvPTqc0EP9T0zUjxCVLBqvvD2e0ugRuarigEmQl0vktb7d7Vp3Bve6J+EI8rKhTDEzhe1XfN0LJTov1Gpo6DzYHq8bnP/4APNxnKPXS+ls5aIXh/I8yvxlO90+CHxyjjsMRm2c33eD+0vwGuGq+X+wT/YOU4TzAllIKlxHlA5kehTgS5WGinl3A8WG5pKE/+gFu3SRg/5sFwKf2E8DyYQaGRMuyMbOfLg4c3NTODyyMxZWx9bc2gtJ/kC52ngsPyBS3X0eyt1BSZliX/NeqTVTEWzMZMiOcf9w== 232 | ``` 233 | 234 | Should result in: 235 | 236 | ``` 237 | data:success 238 | @turingcomplete@ 239 | ``` 240 | 241 | At which point commands can be issued e.g. to scan for another secondary: 242 | 243 | ``` 244 | scan:@bletchleycolossus 245 | ``` -------------------------------------------------------------------------------- /packages/at_repl/doc/api/static-assets/styles.css: -------------------------------------------------------------------------------- 1 | .light-theme { 2 | /*background-color body, listdropdown*/ 3 | --main-bg-color: #fff; 4 | /*header id-tittle*/ 5 | --main-header-color: #eeeeee; 6 | /*package-name*/ 7 | --main-sidebar-color: #727272; 8 | /*section-title and section subtitle, desc markdown(body, dd, h3), header a*/ 9 | --main-text-color: #111111; 10 | /*typehead search-box*/ 11 | --main-search-bar: #fff; 12 | /* scrollbar-thumb */ 13 | --main-scrollbar-color: #CCC; 14 | /* footer */ 15 | --main-footer-background: #111111; 16 | /*header text color*/ 17 | --main-h-text: black; 18 | /* hyperlinks*/ 19 | --main-hyperlinks-color: #0175C2; 20 | /*search background*/ 21 | --main-search-background: transparent; 22 | 23 | /*code snippets*/ 24 | --main-code-bg: #f8f8f8; 25 | --main-keyword-color: #333; 26 | --main-tag-color: #000080; 27 | --main-section-color: #900; 28 | --main-comment-color: #998; 29 | --main-var-color: #008080; 30 | --main-string-color: #d14; 31 | 32 | --main-number-filter: invert(0%); 33 | --main-icon-color: black; 34 | } 35 | 36 | .dark-theme { 37 | /*background-color body, listdropdown*/ 38 | --main-bg-color: #10161E; 39 | /*header id-tittle*/ 40 | --main-header-color: #1C2834; 41 | /*package-name*/ 42 | --main-sidebar-color: #fff; 43 | /*section-title and section subtitle, desc markdown(body, dd, h3), header a*/ 44 | --main-text-color: #fff; 45 | /*typehead search-box*/ 46 | --main-search-bar: #454545; 47 | /* scrollbar-thumb */ 48 | --main-scrollbar-color: #5f6368; 49 | /* footer */ 50 | --main-footer-background: #27323a; 51 | /* hyperlinks*/ 52 | --main-hyperlinks-color: #00D2FA; 53 | /*search background*/ 54 | --main-search-background: black; 55 | 56 | /*code snippets*/ 57 | --main-code-bg: #10161E; 58 | --main-keyword-color: white; 59 | --main-tag-color: #00D2FA; 60 | --main-section-color: #FF2D64; 61 | --main-comment-color: #909CC3; 62 | --main-var-color: #55A09B; 63 | --main-string-color: #FF2D64; 64 | 65 | --main-number-filter: invert(100%); 66 | --main-icon-color: white; 67 | } 68 | 69 | #theme { 70 | display: none; 71 | } 72 | 73 | #theme-button { 74 | position: absolute; 75 | right: 30px; 76 | height: 24px; 77 | } 78 | 79 | #theme-button .material-symbols-outlined { 80 | color: var(--main-icon-color); 81 | user-select: none; 82 | cursor: pointer; 83 | } 84 | 85 | .light-theme #light-theme-button { 86 | display: none; 87 | } 88 | 89 | .dark-theme #dark-theme-button { 90 | display: none; 91 | } 92 | 93 | /* 94 | Only show images that fit their theme using GitHub's syntax, see: 95 | https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/ 96 | */ 97 | .dark-theme img[src$="#gh-light-mode-only"] { 98 | display: none; 99 | } 100 | 101 | .light-theme img[src$="#gh-dark-mode-only"] { 102 | display: none; 103 | } 104 | 105 | /* for layout */ 106 | html, 107 | body { 108 | margin: 0; 109 | padding: 0; 110 | height: 100%; 111 | width: 100%; 112 | overflow: hidden; 113 | box-sizing: border-box; 114 | } 115 | 116 | *, *:before, *:after { 117 | box-sizing: inherit; 118 | } 119 | 120 | body { 121 | display: flex; 122 | flex-direction: column; 123 | -webkit-overflow-scrolling: touch; 124 | } 125 | 126 | header { 127 | flex: 0 0 50px; 128 | display: flex; 129 | flex-direction: row; 130 | align-items: center; 131 | padding-left: 30px; 132 | padding-right: 30px; 133 | background-color: var(--main-header-color); 134 | } 135 | 136 | header ol { 137 | list-style: none; 138 | margin: 0; 139 | padding: 0; 140 | } 141 | 142 | header ol li { 143 | display: inline; 144 | } 145 | 146 | header form { 147 | display: flex; 148 | flex: 1; 149 | justify-content: flex-end; 150 | } 151 | 152 | header#header-search-sidebar { 153 | height: 50px; 154 | margin-bottom: 25px; 155 | } 156 | 157 | footer { 158 | flex: 0 0 16px; 159 | text-align: center; 160 | padding: 16px 20px; 161 | } 162 | 163 | main { 164 | flex: 1; 165 | display: flex; 166 | flex-direction: row; 167 | padding: 20px; 168 | min-height: 0; 169 | } 170 | 171 | .sidebar-offcanvas-left { 172 | flex: 0 1 230px; 173 | order: 1; 174 | overflow-y: scroll; 175 | padding: 20px 0 15px 30px; 176 | margin: 5px 20px 0 0; 177 | } 178 | 179 | ::-webkit-scrollbar-button{ display: none; height: 13px; border-radius: 0; background-color: #AAA; } 180 | ::-webkit-scrollbar-button:hover{ background-color: #AAA; } 181 | ::-webkit-scrollbar-thumb{ background-color: var(--main-scrollbar-color); } 182 | ::-webkit-scrollbar-thumb:hover{ background-color: var(--main-scrollbar-color); } 183 | ::-webkit-scrollbar{ width: 4px; } 184 | 185 | .main-content::-webkit-scrollbar{ width: 8px; } 186 | 187 | .main-content { 188 | flex: 1; 189 | order: 2; 190 | overflow-y: scroll; 191 | padding: 10px 20px 0 20px; 192 | } 193 | 194 | .sidebar-offcanvas-right { 195 | flex: 0 1 12em; 196 | order: 3; 197 | overflow-y: scroll; 198 | padding: 20px 15px 15px 15px; 199 | margin-top: 5px; 200 | margin-right: 20px; 201 | } 202 | /* end for layout */ 203 | 204 | body { 205 | -webkit-text-size-adjust: 100%; 206 | overflow-x: hidden; 207 | font-family: Roboto, sans-serif; 208 | font-size: 16px; 209 | line-height: 1.42857143; 210 | color: var(--main-text-color); 211 | background-color: var(--main-bg-color); 212 | } 213 | 214 | nav.navbar { 215 | background-color: inherit; 216 | min-height: 50px; 217 | border: 0; 218 | } 219 | 220 | @media (max-width: 840px) { 221 | .hidden-xs { 222 | display: none !important; 223 | } 224 | } 225 | 226 | @media (min-width: 841px) { 227 | .hidden-l { 228 | display: none !important; 229 | } 230 | } 231 | 232 | nav.navbar .row { 233 | padding-top: 8px; 234 | } 235 | 236 | nav .container { 237 | white-space: nowrap; 238 | } 239 | 240 | header { 241 | background-color: var(--main-header-color); 242 | box-shadow: 0 3px 5px rgba(0,0,0,0.1); 243 | } 244 | 245 | .pre { 246 | border: 1px solid #ddd; 247 | font-size: 14px; 248 | } 249 | 250 | .hljs-string, .hljs-doctag { 251 | color: var(--main-string-color); 252 | } 253 | 254 | .hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { 255 | color: var(--main-var-color); 256 | } 257 | 258 | .hljs-comment, .hljs-quote { 259 | color: var(--main-comment-color); 260 | font-style: italic; 261 | } 262 | 263 | .hljs-title, .hljs-section, .hljs-selector-id { 264 | color: var(--main-section-color); 265 | font-weight: bold; 266 | } 267 | 268 | .hljs-tag, .hljs-name, .hljs-attribute { 269 | color: var(--main-tag-color); 270 | font-weight: normal; 271 | } 272 | 273 | .hljs-keyword, .hljs-selector-tag, .hljs-subst { 274 | color: var(--main-keyword-color); 275 | font-weight: bold; 276 | } 277 | 278 | .hljs { 279 | display: block; 280 | overflow-x: auto; 281 | padding: 0.5em; 282 | color: var(--main-text-color); 283 | background: var(--main-code-bg); 284 | } 285 | 286 | a { 287 | text-decoration: none; 288 | } 289 | 290 | section { 291 | margin-bottom: 36px; 292 | } 293 | 294 | dl { 295 | margin: 0; 296 | } 297 | 298 | h1, 299 | h2, 300 | h3, 301 | h4, 302 | h5, 303 | h6 { 304 | font-family: Roboto, sans-serif; 305 | font-weight: 400; 306 | margin-top: 1.5em; 307 | color: var(--main-text-color); 308 | } 309 | 310 | h1.title { 311 | overflow: hidden; 312 | text-overflow: ellipsis; 313 | } 314 | 315 | h1 { 316 | font-size: 37px; 317 | margin-top: 0; 318 | margin-bottom: 0.67em; 319 | } 320 | 321 | h2 { 322 | font-size: 28px; 323 | } 324 | 325 | h5 { 326 | font-size: 16px; 327 | } 328 | 329 | p { 330 | margin-bottom: 1em; 331 | margin-top: 0; 332 | } 333 | 334 | a { 335 | color: var(--main-hyperlinks-color); 336 | } 337 | 338 | a:hover { 339 | color: #13B9FD; 340 | } 341 | 342 | pre.prettyprint { 343 | font-family: 'Roboto Mono', Menlo, monospace; 344 | color: black; 345 | border-radius: 0; 346 | font-size: 15px; 347 | word-wrap: normal; 348 | line-height: 1.4; 349 | border: 0; 350 | margin: 16px 0 16px 0; 351 | padding: 8px; 352 | } 353 | 354 | pre code { 355 | white-space: pre; 356 | word-wrap: initial; 357 | font-size: 100% 358 | } 359 | 360 | .fixed { 361 | white-space: pre; 362 | } 363 | 364 | pre { 365 | border: 1px solid #ddd; 366 | background-color: #eee; 367 | font-size: 14px; 368 | } 369 | 370 | code { 371 | font-family: 'Roboto Mono', Menlo, monospace; 372 | color: inherit; 373 | padding: 0.2em 0.4em; 374 | font-size: 85%; 375 | background-color: rgba(27,31,35,0.05); 376 | border-radius: 3px; 377 | } 378 | 379 | @media(max-width: 840px) { 380 | nav .container { 381 | width: 100% 382 | } 383 | 384 | h1 { 385 | font-size: 24px; 386 | } 387 | 388 | pre { 389 | margin: 16px 0; 390 | } 391 | } 392 | 393 | header h1 { 394 | font-weight: 400; 395 | margin-bottom: 16px; 396 | } 397 | 398 | header a, 399 | header p, 400 | header li { 401 | color: #0175C2; 402 | } 403 | 404 | header a:hover { 405 | color: #0175C2; 406 | } 407 | 408 | header h1 .kind { 409 | color: #555; 410 | } 411 | 412 | dt { 413 | font-weight: normal; 414 | } 415 | 416 | dd { 417 | color: var(--main-text-color); 418 | margin-bottom: 1em; 419 | margin-left: 0; 420 | } 421 | 422 | dd.callable, dd.constant, dd.property { 423 | margin-bottom: 24px; 424 | } 425 | 426 | dd p { 427 | overflow-x: hidden; 428 | text-overflow: ellipsis; 429 | margin-bottom: 0; 430 | } 431 | 432 | /* Enum values do not have their own pages; their full docs are presented on the 433 | * enum class's page. */ 434 | dt.constant + dd p { 435 | margin-bottom: 1em; 436 | } 437 | 438 | /* indents wrapped lines */ 439 | section.summary dt { 440 | margin-left: 24px; 441 | text-indent: -24px; 442 | } 443 | 444 | .dl-horizontal dd { 445 | margin-left: initial; 446 | } 447 | 448 | dl.dl-horizontal dt { 449 | font-style: normal; 450 | text-align: left; 451 | color: #727272; 452 | margin-right: 20px; 453 | width: initial; 454 | } 455 | 456 | dt .name { 457 | font-weight: 500; 458 | } 459 | 460 | dl dt.callable .name { 461 | float: none; 462 | width: auto; 463 | } 464 | 465 | .type-parameter { 466 | white-space: nowrap; 467 | } 468 | 469 | .multi-line-signature .type-parameter .parameter { 470 | margin-left: 0; 471 | display: unset; 472 | } 473 | 474 | .parameter-list { 475 | display: table-cell; 476 | margin-left: 10px; 477 | list-style-type: none; 478 | padding-inline-start: unset; 479 | } 480 | 481 | .signature { 482 | color: var(--main-text-color); 483 | } 484 | 485 | .signature a { 486 | color: var(--main-hyperlinks-color); 487 | } 488 | 489 | .optional { 490 | font-style: italic; 491 | } 492 | 493 | .undocumented { 494 | font-style: italic; 495 | } 496 | 497 | .is-const { 498 | font-style: italic; 499 | } 500 | 501 | .deprecated { 502 | text-decoration: line-through; 503 | } 504 | 505 | .category.linked { 506 | font-weight: bold; 507 | opacity: 1; 508 | } 509 | 510 | /* Colors for category based on categoryOrder in dartdoc_options.config. */ 511 | .category.cp-0 { 512 | background-color: #54b7c4 513 | } 514 | 515 | .category.cp-1 { 516 | background-color: #54c47f 517 | } 518 | 519 | .category.cp-2 { 520 | background-color: #c4c254 521 | } 522 | 523 | .category.cp-3 { 524 | background-color: #c49f54 525 | } 526 | 527 | .category.cp-4 { 528 | background-color: #c45465 529 | } 530 | 531 | .category.cp-5 { 532 | background-color: #c454c4 533 | } 534 | 535 | .category a { 536 | color: white; 537 | } 538 | 539 | .category { 540 | padding: 2px 4px; 541 | font-size: 12px; 542 | border-radius: 4px; 543 | background-color: #999; 544 | text-transform: uppercase; 545 | color: white; 546 | opacity: .5; 547 | } 548 | 549 | h1 .category { 550 | vertical-align: middle; 551 | } 552 | 553 | /* The badge under a declaration for things like "const", "read-only", etc. and for the badges inline like Null safety*/ 554 | /* See https://github.com/dart-lang/dartdoc/blob/master/lib/src/model/feature.dart */ 555 | .feature { 556 | display: inline-block; 557 | background: var(--main-bg-color); 558 | border: 1px solid var(--main-hyperlinks-color); 559 | border-radius: 20px; 560 | color: var(--main-hyperlinks-color); 561 | 562 | font-size: 12px; 563 | padding: 1px 6px; 564 | margin: 0 8px 0 0; 565 | } 566 | 567 | a.feature:hover { 568 | border-color: #13B9FD; 569 | } 570 | 571 | h1 .feature { 572 | vertical-align: middle; 573 | } 574 | 575 | .source-link { 576 | padding: 18px 4px; 577 | font-size: 18px; 578 | vertical-align: middle; 579 | } 580 | 581 | @media (max-width: 840px) { 582 | .source-link { 583 | padding: 7px 2px; 584 | font-size: 10px; 585 | } 586 | } 587 | 588 | #external-links { 589 | float: right; 590 | } 591 | 592 | .btn-group { 593 | position: relative; 594 | display: inline-flex; 595 | vertical-align: middle; 596 | } 597 | 598 | footer { 599 | color: #fff; 600 | background-color: var(--main-footer-background); 601 | width: 100%; 602 | } 603 | 604 | footer p { 605 | margin: 0; 606 | } 607 | 608 | footer .no-break { 609 | white-space: nowrap; 610 | } 611 | 612 | footer .container { 613 | padding-left: 0; 614 | padding-right: 0; 615 | } 616 | 617 | footer a, footer a:hover { 618 | color: #fff; 619 | } 620 | 621 | .markdown.desc { 622 | max-width: 700px; 623 | } 624 | 625 | .markdown h1 { 626 | font-size: 24px; 627 | margin-bottom: 8px; 628 | } 629 | 630 | .markdown h2 { 631 | font-size: 20px; 632 | margin-top: 24px; 633 | margin-bottom: 8px; 634 | } 635 | 636 | .markdown h3 { 637 | font-size: 18px; 638 | margin-bottom: 8px; 639 | color: var(--main-text-color); 640 | } 641 | 642 | .markdown h4 { 643 | font-size: 16px; 644 | margin-bottom: 0; 645 | } 646 | 647 | .markdown li p { 648 | margin: 0; 649 | } 650 | 651 | table { 652 | margin-bottom: 1em; 653 | } 654 | 655 | table, 656 | th, 657 | td { 658 | border: 1px solid lightgrey; 659 | border-collapse: collapse; 660 | } 661 | 662 | th, 663 | td { 664 | padding: 8px; 665 | } 666 | 667 | .gt-separated { 668 | list-style: none; 669 | padding: 0; 670 | margin: 0; 671 | } 672 | 673 | .gt-separated li { 674 | display: inline-block; 675 | } 676 | 677 | .gt-separated li:before { 678 | background-image: url("data:image/svg+xml;utf8,"); 679 | background-position: center; 680 | content: "\00a0"; 681 | margin: 0 6px 0 4px; 682 | padding: 0 3px 0 0; 683 | } 684 | 685 | .gt-separated.dark li:before { 686 | background-image: url("data:image/svg+xml;utf8,"); 687 | } 688 | 689 | .gt-separated li:first-child:before { 690 | background-image: none; 691 | content: ""; 692 | margin: 0; 693 | padding: 0; 694 | } 695 | 696 | .multi-line-signature { 697 | font-size: 17px; 698 | color: #727272; 699 | } 700 | 701 | .multi-line-signature .parameter { 702 | margin-left: 24px; 703 | display: block; 704 | } 705 | 706 | .breadcrumbs { 707 | padding: 0; 708 | margin: 8px 0 8px 0; 709 | white-space: nowrap; 710 | line-height: 1; 711 | } 712 | 713 | @media screen and (min-width: 840px) { 714 | nav ol.breadcrumbs { 715 | float: left; 716 | } 717 | } 718 | 719 | @media screen and (max-width: 840px) { 720 | .breadcrumbs { 721 | margin: 0 0 24px 0; 722 | overflow-x: hidden; 723 | } 724 | } 725 | 726 | .breadcrumbs .gt-separated .dark .hidden-xs li+li:before { 727 | color: var(--main-h-text); 728 | } 729 | 730 | ol.breadcrumbs li a { 731 | color: var(--main-hyperlinks-color); 732 | } 733 | 734 | .self-crumb { 735 | color: var(--main-h-text); 736 | } 737 | 738 | .self-name { 739 | color: #555; 740 | display: none; 741 | } 742 | 743 | .annotation-list { 744 | list-style: none; 745 | padding: 0; 746 | display: inline; 747 | } 748 | 749 | .comma-separated { 750 | list-style: none; 751 | padding: 0; 752 | display: inline; 753 | } 754 | 755 | .comma-separated li { 756 | display: inline; 757 | } 758 | 759 | .comma-separated li:after { 760 | content: ", "; 761 | } 762 | 763 | .comma-separated li:last-child:after { 764 | content: ""; 765 | } 766 | 767 | .end-with-period li:last-child:after { 768 | content: "."; 769 | } 770 | 771 | .container > section:first-child { 772 | border: 0; 773 | } 774 | 775 | .constructor-modifier { 776 | font-style: italic; 777 | } 778 | 779 | section.multi-line-signature div.parameters { 780 | margin-left: 24px; 781 | } 782 | 783 | /* sidebar styles */ 784 | 785 | .sidebar ol { 786 | list-style: none; 787 | line-height: 22px; 788 | margin-top: 0; 789 | margin-bottom: 0; 790 | padding: 0 0 15px 0; 791 | } 792 | 793 | .sidebar h5 a, 794 | .sidebar h5 a:hover { 795 | color: var(--main-sidebar-color); 796 | } 797 | 798 | .sidebar h5, 799 | .sidebar ol li { 800 | text-overflow: ellipsis; 801 | overflow: hidden; 802 | padding: 3px 0 3px 3px; 803 | } 804 | 805 | .sidebar h5 { 806 | color: var(--main-sidebar-color); 807 | font-size: 18px; 808 | margin: 0 0 22px 0; 809 | padding-top: 0; 810 | } 811 | 812 | .sidebar ol li.section-title { 813 | font-size: 18px; 814 | font-weight: normal; 815 | text-transform: uppercase; 816 | padding-top: 25px; 817 | } 818 | 819 | .sidebar ol li.section-subtitle a { 820 | color: inherit; 821 | } 822 | 823 | .sidebar ol li.section-subtitle { 824 | font-weight: 400; 825 | text-transform: uppercase; 826 | } 827 | 828 | .sidebar ol li.section-subitem { 829 | margin-left: 12px; 830 | } 831 | 832 | .sidebar ol li:first-child { 833 | padding-top: 3px; 834 | margin-top: 0; 835 | } 836 | 837 | button { 838 | padding: 0; 839 | } 840 | 841 | #sidenav-left-toggle { 842 | display: none; 843 | vertical-align: text-bottom; 844 | padding: 0; 845 | color: var(--main-icon-color); 846 | user-select: none; 847 | cursor: pointer; 848 | } 849 | 850 | /* left-nav disappears, and can transition in from the left */ 851 | @media screen and (max-width:840px) { 852 | #sidenav-left-toggle { 853 | display: inline; 854 | width: 24px; 855 | height: 24px; 856 | border: none; 857 | margin-right: 24px; 858 | margin-left: 24px; 859 | font-size: 24px; 860 | } 861 | 862 | #overlay-under-drawer.active { 863 | opacity: 0.4; 864 | height: 100%; 865 | z-index: 1999; 866 | position: fixed; 867 | top: 0; 868 | left: 0; 869 | right: 0; 870 | bottom: 0; 871 | background-color: black; 872 | display: block; 873 | } 874 | 875 | .sidebar-offcanvas-left { 876 | left: -100%; 877 | position: fixed; 878 | -webkit-transition:all .25s ease-out; 879 | -o-transition:all .25s ease-out; 880 | transition:all .25s ease-out; 881 | z-index: 2000; 882 | top: 0; 883 | width: 280px; /* works all the way down to an iphone 4 */ 884 | height: 90%; 885 | background-color: var(--main-bg-color); 886 | overflow-y: scroll; /* TODO: how to hide scroll bars? */ 887 | padding: 10px; 888 | margin: 10px 10px; 889 | box-shadow: 5px 5px 5px 5px #444444; 890 | } 891 | 892 | ol#sidebar-nav { 893 | font-size: 18px; 894 | white-space: pre-line; 895 | } 896 | 897 | .sidebar-offcanvas-left.active { 898 | left: 0; /* this animates our drawer into the page */ 899 | } 900 | 901 | .self-name { 902 | display: inline-block; 903 | color: var(--main-hyperlinks-color); 904 | } 905 | } 906 | 907 | .sidebar-offcanvas-left h5 { 908 | margin-bottom: 10px; 909 | } 910 | 911 | .sidebar-offcanvas-left h5:last-of-type { 912 | border: 0; 913 | margin-bottom: 25px; 914 | } 915 | 916 | /* the right nav disappears out of view when the window shrinks */ 917 | @media screen and (max-width: 992px) { 918 | .sidebar-offcanvas-right { 919 | display: none; 920 | } 921 | } 922 | 923 | #overlay-under-drawer { 924 | display: none; 925 | } 926 | 927 | /* find-as-you-type search box */ 928 | 929 | .form-control { 930 | border-radius: 0; 931 | border: 0; 932 | } 933 | 934 | @media screen and (max-width: 840px) { 935 | form.search { 936 | display: none; 937 | } 938 | } 939 | 940 | .typeahead { 941 | width: 200px; 942 | padding: 2px 7px 1px 7px; 943 | line-height: 20px; 944 | outline: none; 945 | } 946 | 947 | .tt-wrapper { 948 | position: relative; 949 | display: inline-block; 950 | } 951 | 952 | .tt-input { 953 | position: relative; 954 | vertical-align: top; 955 | } 956 | 957 | .navbar-right .tt-menu { 958 | right: 0; 959 | left: inherit !important; 960 | width: 540px; 961 | max-height: 280px; 962 | overflow-y: scroll; 963 | } 964 | 965 | .navbar-right { 966 | padding-right: 60px; 967 | } 968 | 969 | .tt-menu { 970 | position: absolute; 971 | top: 100%; 972 | left: 0; 973 | z-index: 100; 974 | font-size: 14px; 975 | margin: 0; 976 | background-color: var(--main-bg-color); 977 | border: 1px solid var(--main-header-color); 978 | -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2); 979 | -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2); 980 | box-shadow: 0 5px 10px rgba(0,0,0,.2); 981 | } 982 | 983 | 984 | .typeahead { 985 | padding: 17px 17px 17px 50px; 986 | width: 422px; 987 | height: 20px; 988 | font-size: 13px; 989 | background-image: url("./search.svg"); 990 | background-repeat: no-repeat; 991 | background-position: 4%; 992 | outline: 0; 993 | background-size: 20px; 994 | filter: var(--main-number-filter); 995 | -webkit-filter: var(--main-number-filter); 996 | } 997 | 998 | .search-summary { 999 | margin-bottom: 10px; 1000 | } 1001 | 1002 | a.tt-container { 1003 | font-size: 16px; 1004 | color: var(--main-hyperlinks-color); 1005 | } 1006 | 1007 | .enter-search-message { 1008 | position: -webkit-sticky; 1009 | position: sticky; 1010 | top: 0; 1011 | background-color: #AAA; 1012 | padding: 0; 1013 | font-size: 14px; 1014 | margin: 0; 1015 | clear: both; 1016 | text-align: center; 1017 | color: black; 1018 | } 1019 | 1020 | .tt-suggestion:hover { 1021 | cursor: pointer; 1022 | color: #fff; 1023 | background-color: #0097cf; 1024 | } 1025 | 1026 | .tt-suggestion:hover .search-from-lib { 1027 | color: #ddd; 1028 | } 1029 | 1030 | .tt-suggestion.tt-cursor { 1031 | color: #fff; 1032 | background-color: #0097cf; 1033 | } 1034 | 1035 | .tt-suggestion.tt-cursor .search-from-lib { 1036 | color: #ddd; 1037 | } 1038 | 1039 | .tt-suggestion p { 1040 | margin: 0; 1041 | } 1042 | 1043 | .tt-container { 1044 | font-size: 14px; 1045 | margin-bottom: 0; 1046 | margin-top: 15px; 1047 | } 1048 | 1049 | .tt-container-text { 1050 | color: var(--main-text-color); 1051 | } 1052 | 1053 | 1054 | /* Search results formatting for mini results below search bar. */ 1055 | 1056 | .tt-search-results .tt-container { 1057 | margin-top: 5px; 1058 | margin-bottom: 5px; 1059 | } 1060 | 1061 | /* Do not show the container as a section. */ 1062 | .tt-search-results .tt-container-text { 1063 | display: none 1064 | } 1065 | 1066 | /* An inline style. */ 1067 | .tt-search-results .tt-suggestion { 1068 | color: var(--main-text-color); 1069 | margin-top: 5px; 1070 | overflow: hidden; 1071 | padding-left: 10px; 1072 | padding-right: 10px; 1073 | text-overflow: ellipsis; 1074 | white-space: nowrap; 1075 | } 1076 | 1077 | .tt-search-results .tt-suggestion-title { 1078 | font-size: 14px; 1079 | padding-right: 5px; 1080 | } 1081 | 1082 | .tt-search-results .tt-suggestion-container { 1083 | color: var(--main-keyword-color); 1084 | font-size: 14px; 1085 | font-style: italic; 1086 | padding-right: 5px; 1087 | } 1088 | 1089 | .tt-search-results .one-line-description { 1090 | color: var(--main-keyword-color); 1091 | display: inline; 1092 | margin-left: 0; 1093 | } 1094 | 1095 | 1096 | .tt-search-results .one-line-description::before { 1097 | content: open-quote; 1098 | } 1099 | 1100 | .tt-search-results .one-line-description::after { 1101 | content: close-quote; 1102 | } 1103 | 1104 | /* Search results formatting for `search.html`. */ 1105 | 1106 | /* A block style. */ 1107 | #dartdoc-main-content .tt-suggestion { 1108 | color: var(--main-text-color); 1109 | margin-top: 5px; 1110 | margin-bottom: 10px; 1111 | border-style: solid; 1112 | border-color: lightgrey; 1113 | border-width: 0.5px; 1114 | } 1115 | 1116 | #dartdoc-main-content .tt-suggestion-title { 1117 | display: block; 1118 | font-weight: 500; 1119 | margin: 4px 10px 0; 1120 | } 1121 | 1122 | #dartdoc-main-content .one-line-description { 1123 | display: block; 1124 | margin: 2px 10px 3px; 1125 | } 1126 | 1127 | /* Do not show a result's container. */ 1128 | #dartdoc-main-content .tt-suggestion-container { 1129 | display: none; 1130 | } 1131 | 1132 | @media screen and (max-width: 840px) { 1133 | .typeahead { 1134 | padding: 17px 17px 17px 33px; 1135 | width: 240px; 1136 | height: 17px; 1137 | border: 1px solid #f5f5f5; 1138 | background-position: 3%; 1139 | margin: 10px 10px 10px 9px; 1140 | } 1141 | 1142 | header { 1143 | padding-left: 0; 1144 | } 1145 | } 1146 | 1147 | @media screen and (max-width: 320px) { 1148 | #sidenav-left-toggle { 1149 | margin-right: 10px; 1150 | margin-left: 20px; 1151 | } 1152 | 1153 | .self-name { 1154 | margin-right: 10px; 1155 | } 1156 | } 1157 | 1158 | ::placeholder { 1159 | filter: brightness(0.85); 1160 | } 1161 | 1162 | .search-body { 1163 | border: 1px solid #7f7f7f; 1164 | max-width: 400px; 1165 | box-shadow: 3px 3px 5px rgba(0,0,0,0.1); 1166 | } 1167 | 1168 | section#setter { 1169 | border-top: 1px solid #ddd; 1170 | padding-top: 36px; 1171 | } 1172 | 1173 | li.inherited a { 1174 | opacity: 0.65; 1175 | font-style: italic; 1176 | } 1177 | 1178 | #instance-methods dt.inherited .name, 1179 | #instance-properties dt.inherited .name, 1180 | #operators dt.inherited .name { 1181 | font-weight: 400; 1182 | font-style: italic; 1183 | } 1184 | 1185 | #instance-methods dt.inherited .signature, 1186 | #instance-properties dt.inherited .signature, 1187 | #operators dt.inherited .signature { 1188 | font-weight: 400; 1189 | } 1190 | 1191 | @media print { 1192 | .subnav, .sidebar { 1193 | display: none; 1194 | } 1195 | 1196 | a[href]:after { 1197 | content: "" !important; 1198 | } 1199 | } --------------------------------------------------------------------------------