Giter Club home page Giter Club logo

quill's Introduction

quill

Minimalistic ledger and governance toolkit for cold wallets.

quill is a toolkit for interacting with the Network Nervous System's (NNS) canisters using self-custody keys. These keys can be held in an air-gapped computer (a computer that has never connected to the internet) known as a cold wallet. To support cold wallets, quill takes a two-phase approach to sending query/update calls to the IC. In the first phase, quill is used with the various subcommands to generate and sign messages based on user input, without needing access to the internet. In the second phase, the signed message(s) are sent to the IC. Since this requires connection to boundary nodes via the internet, cold-wallet users will transport the signed message(s) from the air-gapped computer (i.e. with a USB stick, or via QR code) to a computer connected to the internet.

Disclaimer

YOU EXPRESSLY ACKNOWLEDGE AND AGREE THAT USE OF THIS SOFTWARE IS AT YOUR SOLE RISK. AUTHORS OF THIS SOFTWARE SHALL NOT BE LIABLE FOR DAMAGES OF ANY TYPE, WHETHER DIRECT OR INDIRECT.

Usage

This will sign a transfer transaction and print to STDOUT:

quill transfer <account-id> --amount <amount> --pem-file <path>

To display the signed message in human-readable form:

quill send --dry-run <path-to-file>

quill could be used on an online computer to send any signed transaction:

quill send <path-to-file>

To get the principal and the account id:

quill public-ids --pem-file <path>

Governance

This is how you’d stake/top-up a neuron:

quill neuron-stake --amount 2.5 --name 1 --pem-file <path>

Managing the neuron:

quill neuron-manage <neuron-id> [OPERATIONS] --pem-file <path>

All the commands above will generate signed messages, which can be sent on the online machine using the send command from above.

Download & Install

Use pre-built binaries from the latest release.

MacOS (Intel Chip & Apple Silicon)

Install quill

  1. Download the file named quill-macos-x86_64
  2. Move the file to your /usr/local/bin directory to make it available system-wide
sudo mv quill-macos-x86_64 /usr/local/bin/quill
  1. Make the file executable
chmod +x /usr/local/bin/quill
  1. Run quill
quill -h

Linux

  1. Download the file specific to your system architecture

    1. for x86 download quill-linux-x86_64
    2. for arm32 download quill-arm_32
    3. for Alpine download quill-linux-x86_64-musl
  2. Move the file to your /usr/local/bin directory to make it available system-wide

sudo mv quill-linux-x86_64 /usr/local/bin/quill
  1. Make the file executable
chmod +x /usr/local/bin/quill 
  1. Run quill
quill -h

Windows

  1. Download the file named quill-windows-x86_64.exe

  2. Move it and a shell to a convenient location, e.g.

move-item quill-windows-x86_64.exe ~\quill.exe
set-location ~
  1. Run quill
.\quill.exe -h

Build

To compile quill run:

cargo build --release --locked

After this, find the binary at target/release/quill.

Quill has two optional features, all activated by default:

  • hsm, to enable PKCS#11 HSM support (requires runtime dynamic linking)
  • ledger, to enable Ledger Nano support (requires runtime dynamic linking, and incompatible with armv6)

To build a version of Quill compatible with statically-linked-only environments, such as Alpine, run:

cargo build --release --locked --no-default-features

Building with Docker/Podman

Quill can be reproducibly built or cross-compiled in a Docker container using cross.

  1. Follow the instructions at cross-rs/cross to install cross.
  2. If using a target with particular restrictions, such as x86_64-apple-darwin or x86_64-pc-windows-msvc, ensure you have built a local image via the instructions at cross-rs/cross-toolchains.
  3. Run cross build --release --locked --target <target platform>, e.g. --target x86_64-unknown-linux-gnu or --target armv7-unknown-linux-gnueabihf.

Testnets

If you have access to an Internet Computer testnet (for example, a version the replica binary and NNS running locally), you can target quill at this test network by setting the IC_URL environment variable to the full URL. In addition to that, it is required to use the --insecure-local-dev-mode flag. For example:

IC_URL=https://nnsdapp.dfinity.network quill --insecure-local-dev-mode --pem-file <path> list-neurons

Contribution

Contributions to Quill are welcomed! For information about contributing, see CONTRIBUTING.md. Contributors must agree to a CLA.

Credit

Originally forked from the SDK.

quill's People

Contributors

adamspofford-dfinity avatar anchpop avatar arcz avatar bjoernek avatar chenyan-dfinity avatar chenyan2002 avatar chmllr avatar danielthurau avatar dependabot[bot] avatar dfx-json avatar ericswanson-dfinity avatar ggreif avatar jean-dfinity avatar jessiemongeon1 avatar jplevyak avatar jwiegley avatar luismompohanden avatar lwshang avatar mariodfinity avatar mraszyk avatar ninegua avatar olaszakos avatar rstout avatar sesi200 avatar

Stargazers

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

Watchers

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

quill's Issues

quill cannot send on zsh

It might be related to zsh: the observed behavior is that it prompts for [y/N] but doesn't wait for a key press. Instead it terminates and the message does not get sent.

Rename --clear-manage-neuron-followees

The subcommand neuron-manage has an option called --clear-manage-neuron-followees. Given that the subcommand is already called neuron-manage that is a repetition. Better names would be --clear-all-followees or -unfollow-all.

"Months" have weak meaning when it comes to dissolve delays

In all of our literature we talk about "6 months" being the minimum dissolve delay that can accrue voting rewards, except that we use seconds in the quill user interface and don't specify which six months, in which year, are being used to measure those seconds.

We should both document that 15_778_800 seconds is the actual minimum number of seconds required to vote, and also provide a constant string such as SIX_MONTHS that can be passed in the place of a seconds figure, to make it easier for users. Better still would be to add a simple expression parser that handles basic arithmetic, and then make all the same constants we use in Rust available, e.g.:

quill neuron-manage --additional-dissolve-delay-seconds '(8 * MONTHS) + 2 YEARS'

arm64 releases?

Thanks for the release!

Will amd64 arm64 be also supported?

Support multiple authentication methods

Right now we use PEM files to provide an identity for signing requests, but we'd also like to support NitroHSM (for node providers) and the Ledger Nano, and then possibly more. I propose to do this by splitting the core governance related code into a new, internal ink crate, and use the quill crate to define the CLI and its behavior, and then have multiple auth-* crates referenced by quill to do the actual work of working with the device to sign requests.

Linux build for the HSM release of quill does not work

Right now we build quill on Linux fully static, which means that the binary cannot load shared libraries. This prevents the HSM variant of quill from being able to load opensc-pkcs11.so.

Either we need to link pkcs11 statically, or release quill on Linux as not a fully-static binary.

Since I'm not quite conversant on how the GitHub action that we build our release binaries with sets up its dependencies, can you assist with this @chmllr?

command is null when calling neuron-manage

Issuing this command and sending the resulting JSON payload

./target/release/quill neuron-manage --split AMOUNT NEURON_ID

results in command = null

Sending message with

  Call type:   update
  Sender:      PRINCIPAL_ID
  Canister id: rrkah-fqaaa-aaaaa-aaaaq-cai
  Method name: manage_neuron
  Arguments:   (
  record {
    id = opt record { id = NEURON_ID : nat64 };
    command = null;
    neuron_id_or_subaccount = null;
  },
)

This then returns an error from the IC

The Replica returned an error: code 5, message: "Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: Panicked at 'explicit panic', /ic/rs/nns/governance/src/governance.rs:5949:21"

I get this problem with any neuron-manage sub-command.

Update Usage Examples in README

In release 0.3.0 of quill a feature change was made that requires args to be moved to the end of the quill command. However, all the examples in the Usage and Governance sections of the README still show the --pem-file <path> in the middle of the command. Can these examples be updated? The commands don't seem to work unless the pem file path is provided at the end.

macOS refers to binaries that users likely do not have installed

After downloading the macOS binary:

Vulcan ~/dl $ otool -L quill-macos-x86_64
quill-macos-x86_64:
	/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 59306.140.5)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1677.104.0)
	/usr/local/opt/[email protected]/lib/libssl.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/usr/local/opt/[email protected]/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
	/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)

These libssl and libcrypto paths are not likely to be present on most users' machines. We need to find a way of packaging these with the release, or use an external dependency that the user will know how to install.

Allow disburse to other account than controller

neuron-manage --disburse disburses to the controller's account.

That's great because it is always safe to do so. But there's a privacy issue. Disbursing from multiple neurons which are controlled by the same controller unnecessarily links their ledger accounts together.

neuron-manage should accept neuron id with _ separator

From dfx we are used to read and provide nat64s (such as neuron ids) with a bunch of _ separators inside them. It would be great if neuron ids could be copy-pasted from candid output and be accepted as is by quill neuron-manage.

--follow-neurons parsing error

When --follow-neurons is the last argument, only followed by <NEURON_ID> of the neuron that is being managed, then the command line parser seems to interpret <NEURON_ID> as just another followee and complains that <NEURON_ID> is missing.

A work-around is to put --follow-topic between the followees and <NEURON_ID>.

cant generate key from seed

i have a seed phrase in phrase.txt and try to execute the following command
quill generate --phrase "$(< phrase.txt)" --pem-file identity.pem
this is the error i receive

❯ quill generate --phrase "$(< phrase.txt)" --pem-file identity.pem
Error: Couldn't read PEM file

Caused by:
    No such file or directory (os error 2)

Allow to disburse partial amount

neuron-manage --disburse currently disburses the entire amount.

It is helpful to be able to disburse a partial amount, for example as a test transaction. (At the latest this feature becomes necessary if #43 is implemented.)

Make --name accept empty string

The neuron-stake command takes a neuron name and converts it to a nonce. It would be natural if the empty string was converted to the nonce value 0. Currently, clap seems to prevent the empty string from being accepted. It would be good to remove that restriction.

list-proposals does not return "deadline_timestamp_seconds"

When calling the "list_proposals" method of https://bmht6-iiaaa-aaaad-qabeq-cai.raw.ic0.app/principal/rrkah-fqaaa-aaaaa-aaaaq-cai I can see that deadline_timestamp_seconds is returned in the response (see below). This is missing when invoking the same method via quill.

"proposal_info": [
{
"id": [
{
"id": "43574"
}
],
"status": 1,
"topic": 8,
"failure_reason": [],
"ballots": [],
"proposal_timestamp_seconds": "1644281103",
"reward_event_round": "0",
"deadline_timestamp_seconds": [
"1644626703"
],

Error after installing quill on M1 mac

I'm on an M1 mac and I've downloaded the latest release. After making the binary executable ('sudo chmod +x quill-macos-x86_64'), I get the following error when I try to run quill:

dyld: Library not loaded: /usr/local/opt/[email protected]/lib/libssl.1.1.dylib
  Referenced from: /Users/[...]/./quill-macos-x86_64
  Reason: image not found
zsh: abort      ./quill-macos-x86_64

I have openssl installed via brew and it's at the following version:

 % openssl
OpenSSL> version
LibreSSL 2.8.3

Could someone help me get quill to run?

Quill candid files are out of date with new SNS/NNS versions

For example, trying to create a MintSnsTokens proposal is broken:

❯ quill sns make-proposal "016709e02f2466d9cfafc13fcc549cfb3d59f142c2996d2f18c94a3edbe93570" --proposal '(
    record {
        title = "Mint 41100 ICP to Foo Labs";
        url = "https://sns-examples.com/proposal/42";
        summary = "Mint 411 ICP to Foo Labs";
        action = opt variant {
            MintSnsTokens = record {

                to_principal = opt principal "ozcnp-xcxhg-inakz-sg3bi-nczm3-jhg6y-idt46-cdygl-ebztx-iq4ft-vae";

                to_subaccount = null;

                memo = null;

                amount_e8s = opt 4_110_000_000_000 : opt nat64;
            }
        }
    }
)' --canister-ids-file canister-ids.json > msg.json

FIX ME! opt table2 <: opt variant {
  ManageNervousSystemParameters : record {
    default_followees : opt record {
      followees : vec record {
        nat64;
        record { followees : vec record { id : vec nat8 } };
      };
    };
    max_dissolve_delay_seconds : opt nat64;
    max_dissolve_delay_bonus_percentage : opt nat64;
    max_followees_per_function : opt nat64;
    neuron_claimer_permissions : opt record { permissions : vec int32 };
    neuron_minimum_stake_e8s : opt nat64;
    max_neuron_age_for_age_bonus : opt nat64;
    initial_voting_period_seconds : opt nat64;
    neuron_minimum_dissolve_delay_to_vote_seconds : opt nat64;
    reject_cost_e8s : opt nat64;
    max_proposals_to_keep_per_action : opt nat32;
    wait_for_quiet_deadline_increase_seconds : opt nat64;
    max_number_of_neurons : opt nat64;
    transaction_fee_e8s : opt nat64;
    max_number_of_proposals_with_ballots : opt nat64;
    max_age_bonus_percentage : opt nat64;
    neuron_grantable_permissions : opt record { permissions : vec int32 };
    voting_rewards_parameters : opt record {
      final_reward_rate_basis_points : opt nat64;
      initial_reward_rate_basis_points : opt nat64;
      reward_rate_transition_duration_seconds : opt nat64;
      round_duration_seconds : opt nat64;
    };
    maturity_modulation_disabled : opt bool;
    max_number_of_principals_per_neuron : opt nat64;
  };
  AddGenericNervousSystemFunction : record {
    id : nat64;
    name : text;
    description : opt text;
    function_type : opt variant {
      NativeNervousSystemFunction : record {};
      GenericNervousSystemFunction : record {
        validator_canister_id : opt principal;
        target_canister_id : opt principal;
        validator_method_name : opt text;
        target_method_name : opt text;
      };
    };
  };
  RemoveGenericNervousSystemFunction : nat64;
  UpgradeSnsToNextVersion : record {};
  RegisterDappCanisters : record { canister_ids : vec principal };
  TransferSnsTreasuryFunds : record {
    from_treasury : int32;
    to_principal : opt principal;
    to_subaccount : opt record { subaccount : vec nat8 };
    memo : opt nat64;
    amount_e8s : nat64;
  };
  UpgradeSnsControlledCanister : record {
    new_canister_wasm : vec nat8;
    mode : opt int32;
    canister_id : opt principal;
    canister_upgrade_arg : opt vec nat8;
  };
  DeregisterDappCanisters : record {
    canister_ids : vec principal;
    new_controllers : vec principal;
  };
  Unspecified : record {};
  ManageSnsMetadata : record {
    url : opt text;
    logo : opt text;
    name : opt text;
    description : opt text;
  };
  ExecuteGenericNervousSystemFunction : record {
    function_id : nat64;
    payload : vec nat8;
  };
  Motion : record { motion_text : text };
} via special opt rule.
This means the sender and receiver type has diverged, and can cause data loss.

BUG? Submit proposal is not executed with send.

Current Problem

When I submit a proposal using make-proposal and send command in sns-quill, I get an error, but if I call manage_neuron of sns_governance canister directly, the proposal is accepted.
In the case of make-upgrade-canister-proposal, I would like to submit a proposal from sns-quill if possible because wasm binaries are huge. How can I do that? Also I get an error in sns-quill make-upgrade-canister-proposal.

When I run make-proposal and send command

$ IC_URL=http://127.0.0.1:8080/ sns-quill \
  --canister-ids-file ./canister_ids.json \
  --pem-file ~/.config/dfx/identity/$(dfx identity whoami)/identity.pem \
  make-proposal 6a9a0cf8dd0a807e1a1e7dd4204090f8f16b12e572201f3ab157ca0fcaffc495 \
  --proposal \
  '( 
    record { 
        title="Launch SNS";
        url="https://dfinity.org"; 
        summary="A motion to launch the SNS";
        action=opt variant { 
            Motion=record { 
                motion_text="I hereby raise the motion that the use of the SNS shall commence"; 
            } 
        };  
    } 
  )' \
  > msg.json \
&& IC_URL=http://127.0.0.1:8080/ sns-quill send msg.json --yes

Sending message with

  Call type:   update
  Sender:      teejb-e422d-rmi2d-ngcen-qlu5a-wbxuh-f6fiv-xerij-pxein-pw4r3-2qe
  Canister id: q3fc5-haaaa-aaaaa-aaahq-cai
  Method name: manage_neuron
  Arguments:   (
  record {
    subaccount = blob "j\9a\0c\f8\dd\0a\80~\1a\1e}\d4 @\90\f8\f1k\12\e5r \1f:\b1W\ca\0f\ca\ff\c4\95";
    command = opt variant {
      MakeProposal = record {
        url = "https://dfinity.org";
        title = "Launch SNS";
        action = opt variant {
          Motion = record {
            motion_text = "I hereby raise the motion that the use of the SNS shall commence";
          }
        };
        summary = "A motion to launch the SNS";
      }
    };
  },
)
Request ID: 0x248778a621ec49eeb55e72d0bf26c1c3f2f77feddf1b5730d34135226cd5524b
The request is being processed...
The request is being processed...
[Canister q3fc5-haaaa-aaaaa-aaahq-cai] [Governance] manage_neuron
Response: ManageNeuronResponse { command: Some(Error(GovernanceError { error_type: InvalidProposal, error_message: "1 defects in Proposal:\nNo action was specified." })) }

When I run it directly

dfx canister call sns_governance manage_neuron \
'(
  record {
    subaccount = blob "j\9a\0c\f8\dd\0a\80~\1a\1e}\d4 @\90\f8\f1k\12\e5r \1f:\b1W\ca\0f\ca\ff\c4\95";
    command = opt variant {
      MakeProposal = record {
        url = "https://dfinity.org";
        title = "Launch SNS";
        action = opt variant {
          Motion = record {
            motion_text = "I hereby raise the motion that the use of the SNS shall commence";
          }
        };
        summary = "A motion to launch the SNS";
      }
    };
  },
)'

[Canister q3fc5-haaaa-aaaaa-aaahq-cai] [Governance] manage_neuron
(
  record {
    command = opt variant {
      MakeProposal = record { proposal_id = opt record { id = 2 : nat64 } }
    };
  },
)

Support account and neuron balance queries

Often when building confidence in staking operations, one wants to check account and neuron balances both before and after a command. Since quill is useful for all other aspects of neuron staking, it should cover these queries as well.

For accounts, I imagine:

quill balance $ACCOUNT

For neurons:

quill neuron-balance $NEURON

This would report a summary of: staked ICP, dissolve delay, dissolve state, age and maturity-in-ICP.

Example command error in quill docs

Bug location: quill/docs/cli-reference/quill-neuron-manage.md
Error statement: quill neuron-manage 2313380519530470538--additional-dissolve-delay-seconds 31536000
It should be: quill neuron-manage 2313380519530470538 --additional-dissolve-delay-seconds 31536000

Need to add spaces

Support a --yes option to answer "yes" to all queries

At the moment we query interactively with a y/N prompt to determine if the user wishes to submit a transaction to the network. To facilitate scripting solutions, we should allow a --yes option that assumes the answer is yes to all such queries.

Add Additional Install Instructions to the README

I recently set up a new computer and came to this repository to get the latest version of quill. I had trouble with the installation that required me to get assistance from @chmllr. The solution was relatively simple and I thought it might be helpful to document it in the README.md file.

I first attempted to Build the quill file using rustup as well as Nix according to the instructions. In both cases I ran into errors even though I installed the latest version of both rustup and Nix for this purpose. I didn't troubleshoot further since I assumed I could just copy/paste the binary.

I am running the Ubuntu distribution of Linux on Windows with WSL2. Hence, I downloaded the quill-linux-x86_64 binary from the release link in the Download section. Once on my computer, I changed the filename to simply quill and moved it into the Ubuntu\home\<user>\bin folder, which is where the dfx binary is also installed. After closing the terminal and opening a new terminal, I tried running the quill --version command and received an error (see image below). This is where I got stuck and needed to reach out to Christian. He immediately recognized that I needed to run the chmod +x ./quill command in order to change the permissions assigned to the file by default. At my current level of developer experience, I would not have figured this out on my own. Hence, it would be nice if this instruction could be provided in the Download section of the README file.

image

Re-sign status request

quill signs both the ingress message and the status request. Both have the same expiry. It is possible that the ingress message is submitted before the expiry and successfully processed but the status request not. If this happens we need a way to reproduce and re-sign the status request with a new expiry.

Problems building in a Nix sandbox

I'm trying to build quill using the following Nix derivation:

quill = with pkgs; rustPlatform.buildRustPackage rec {
  name = "quill-${version}";
  version = "0.0.0-unknown";

  src = fetchFromGitHub {
    owner = "dfinity";
    repo = "quill";
    rev = "0baa53c175a831c0ad1a8c29b2dc0f437d105530";
    sha256 = "1igggarq4kyln8hzsbpamz5q2hgh5d3bfkm8vfmafh013x4h55bf";
    # date = 2021-06-18T08:16:07+02:00;
  };

  cargoSha256 = "1sv8zqj8a2ykz7c2fbdp39gzcqnz1mq6h9bs4x68mc9cs1fsb9zv";

  nativeBuildInputs = [ pkg-config ];
  buildInputs = [ openssl.dev protobuf ]
    ++ lib.optional stdenv.isDarwin libiconv;
};

However, the Rust build fails with the following error:

error: failed to run custom build command for `ic-registry-transport v0.8.0 (https://github.com/dfinity/ic?rev=779549eccfcf61ac702dfc2ee6d76ffdc2db1f7f#779549ec)`

Caused by:
  process didn't exit successfully: `/private/tmp/nix-build-quill-0.0.0-unknown.drv-0/source/target/release/build/ic-registry-transport-3a9c9d520a265434/build-script-build` (exit code: 101)
  --- stdout
  cargo:rerun-if-changed=proto/ic_registry_transport/pb/v1/transport.proto

  --- stderr
  thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "protoc failed: ../../protobuf/def: warning: directory does not exist.\nmessaging/xnet/v1/mixed_hash_tree.proto: File not found.\nic_registry_transport/pb/v1/transport.proto:30:1: Import \"messaging/xnet/v1/mixed_hash_tree.proto\" was not found or had errors.\nic_registry_transport/pb/v1/transport.proto:216:3: \"messaging.xnet.v1.MixedHashTree\" is not defined.\n" }', /private/tmp/nix-build-quill-0.0.0-unknown.drv-0/quill-0.0.0-unknown-vendor.tar.gz/ic-registry-transport/build.rs:30:10
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Are there any other steps required before beginning what is effectively just a cargo build?

New subcommand for calling get_full_neuron

Calling get_full_neuron is not possible with the dfx sign/send combo. That is because the dfx send part will only return a request id and another signed request_status is required. So this seems like another task for quill.

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.