Git Product home page Git Product logo

terraform-provider-pass's Introduction

Pass Terraform Provider

Terraform Registry Version Go Report Card Build Status By Camptocamp

This provider adds integration between Terraform and Pass and Gopass password stores.

Pass is a password store using gpg to encrypt password and git to version. Gopass is a rewrite of the pass password manager in Go with the aim of making it cross-platform and adding additional features.

Requirements

Building The Provider

Download the provider source code

$ go get github.com/camptocamp/terraform-provider-pass

Enter the provider directory and build the provider

$ cd $GOPATH/src/github.com/camptocamp/terraform-provider-pass
$ dep ensure
$ make build

Installing the provider

After building the provider, install it using the Terraform instructions for installing a third party provider.

Example

provider "pass" {
  store_dir = "/srv/password-store"    # defaults to $PASSWORD_STORE_DIR
  refresh_store = false                # do not call `git pull`
}


resource "pass_password" "test" {
  path = "secret/foo"
  password = "0123456789"
  data = {
    zip = "zap"
  }
}

data "pass_password" "test" {
  path = "${pass_password.test.path}"
}

Usage

The pass provider

Argument Reference

The provider takes no arguments.

The pass_password resource

Argument Reference

The resource takes the following arguments:

  • path - Full path from which a password will be read
  • password - Secret password
  • data - (Optional) Additional secret data (keys and values, not nested)
  • yaml - (Optional) YAML document, can't be set together with data

Attribute Reference

The following attributes are exported:

  • path - Full path from which the password was read
  • password - Secret password
  • data - Additional secret data
  • body - Raw secret data, only filled if not stored as YAML
  • full - Entire raw secret contents

The pass_password data source

Argument Reference

The data source takes the following arguments:

  • path - Full path from which a password will be read

Attribute Reference

The following attributes are exported:

  • path - Full path from which the password was read
  • password - Secret password
  • data - Additional secret data
  • body - Raw secret data, only filled if not stored as YAML
  • full - Entire raw secret contents

Developing the Provider

If you wish to work on the provider, you'll first need Go installed on your machine (version 1.21+ is required). You'll also need to correctly setup a GOPATH, as well as adding $GOPATH/bin to your $PATH.

To compile the provider, run make build. This will build the provider and put the provider binary in the $GOPATH/bin directory.

$ make bin
...
$ $GOPATH/bin/terraform-provider-$PROVIDER_NAME
...

In order to test the provider, you can simply run make test.

$ make test

In order to run the full suite of Acceptance tests, run make testacc.

Note: Acceptance tests create real resources, and often cost money to run.

$ make testacc

terraform-provider-pass's People

Contributors

bassco avatar cryptobioz avatar elkiwos avatar hbollon avatar idavidmcdonald avatar jaceq avatar jakubgs avatar mcanevet avatar mkjmdski avatar philandstuff avatar phillipj avatar rafaelfelix avatar raphink avatar toabi avatar yann-soubeyrand 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  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

terraform-provider-pass's Issues

Datasource pass_password fails if value is not a string in yaml

The pass_password provider fails if the value of the yaml secret as something else than a string.
For example:

mysecret
---
user: myuser
password: mysecret
foo: 1

Will fail with:

expected type 'string', got unconvertible type 'int'

The same problem occurs if the value is a map.

Arm64 (Apple Silicon) support

Hi everybody,

Is this project still alive? Not seeing much activity here lately.

I would love to see this project updated to support running the plugin on arm64 (Apple Silicon). I can help out with this if needed, but I am not much of a golang expert.

Error installing package with Golang 1.13

After upgrade Golang I get an error when trying to install this package:

$ go get github.com/camptocamp/terraform-provider-pass
package github.com/vmihailenco/msgpack/codes: cannot find package "github.com/vmihailenco/msgpack/codes" in any of:
	/usr/local/Cellar/[email protected]/1.13.15/libexec/src/github.com/vmihailenco/msgpack/codes (from $GOROOT)
	/Users/benthorner/go/src/github.com/vmihailenco/msgpack/codes (from $GOPATH)
make: *** [get-pass-provider] Error 1

$ go version
go version go1.13.15 darwin/amd64

I think this is the same as this issue: Telmate/terraform-provider-proxmox#290. The workaround described there works for me. Does anyone know if a longer-term fix is possible?

[ERROR] Failed to instantiate provider "pass" to obtain schema: fork/exec

Hi,

I'm trying to create a docker image with terraform, terragrunt, passwordstore and terraform-provider-pass and I have this error :

[terragrunt] 2020/07/31 12:01:44 Running command: terraform plan

Error: Failed to instantiate provider "pass" to obtain schema: fork/exec /app/tests/.terraform/plugins/linux_amd64/terraform-provider-pass: no such file or directory
$ /app/tests/.terraform/plugins/linux_amd64/terraform-provider-pass
/bin/sh: /app/tests/.terraform/plugins/linux_amd64/terraform-provider-pass: not found
$ ls /app/tests/.terraform/plugins/linux_amd64/
$ ls -l /app/tests/.terraform/plugins/linux_amd64/
total 215640
-rwxr-xr-x    1 1000     1000           485 Jul 31 10:31 lock.json
-rwxr-xr-x    1 1000     1000      20427104 Jul 31 10:31 terraform-provider-external_v1.2.0_x4
-rwxr-xr-x    1 1000     1000      60207104 Jul 31 10:30 terraform-provider-google-beta_v3.32.0_x5
-rwxr-xr-x    1 1000     1000      58187776 Jul 31 10:31 terraform-provider-google_v3.32.0_x5
-rwxr-xr-x    1 1000     1000      20373856 Jul 31 10:31 terraform-provider-null_v2.1.2_x4
-rwxrwxr-x    1 1000     1000      39895105 Jul 24 12:56 terraform-provider-pass
-rwxr-xr-x    1 1000     1000      21704704 Jul 31 10:31 terraform-provider-random_v2.3.0_x4

pass show xxxxx works well.

No problem on host system (terragrunt plan success) :

$ ~/.terraform.d/plugins/terraform-provider-pass 
This binary is a plugin. These are not meant to be executed directly.
Please execute the program that consumes these plugins, which will
load any plugins automatically

I've tryed this images with no success :

  • golang:buster
  • debian:10-slim
  • hashicorp/terraform

Installing last version of all tools (terraform, terragrunt pass, terraform-provider-pass) no success too.

Any idea ?

Thanks

DR

long value strings with spaces gets converted to newlines with double space prefix

resource "pass_password" "testspaces" {
  path = "secret/foo"
  password = "0123456789"
  data = {
    zip = "zap"
    tf_provider = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbb cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
 ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
  }
}

Applying:

$ terraform apply  -auto-approve
pass_password.testspaces: Creating...
  data.%:           "" => "2"
  data.tf_provider: "" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
  data.zip:         "" => "zap"
  password:         "" => "0123456789"
  path:             "" => "secret/foo"
pass_password.testspaces: Creation complete after 4s (ID: secret/foo)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
$

Looks good, but upon inspection:

$ gopass secret/foo | grep -n '^'
1:0123456789
2:---
3:tf_provider: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
4:  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
5:  cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
6:  ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
7:zip: zap
$

Interestingly, multi-line entries are fixed by running gopass insert on another token in the secret:

$ gopass secret/foo | grep -n '^'
1:0123456789
2:---
3:tf_provider: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
4:  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
5:  cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
6:  ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
7:zip: zap
$ gopass insert secret/foo zop
secret/foo:zop []: bof
<redacted gpg details>
Do you want to continue? [Y/n/q]: y
Pushed changes to git remote
$

$ gopass secret/foo | grep -n '^'
1:0123456789
2:tf_provider: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
3:zip: zap
4:zop: bof

In case that's useful/related.

Update the README

the README need to be updated to remove the now obsolete options in the provider, as they return a error using version >=2.0

and add the now required provider source example

terraform {
  required_providers {
    pass = {
      source  = "camptocamp/pass"
      version = "~> 2.0.0"
    }
  }
}

Windows x64 compile

There is a compiled release for x64 Linux for download here. Could anyone add x64 Windows compiles to the release process, please?
If not: Could anyone provide a current Windows x64 build?

Thank you very much,
ms

decrypt pass once before running terraform plan/apply

When pass never has decrypted a given store, terraform plan fails with "failed to decrypt" error message. After manually doing a pass ls /path/to/key and enter the GPG password, it works fine.

Maybe a function should be implemented in the terraform data-source doing a pass ls command silently first to make GPG asking for a password once.

refresh_store = true breaks in some circumstances

So refresh_store = true breaks if following conditions are met:
-> store_dir isn't defined
-> root store has no remote git
-> substore / mount has remote git (and I'd like to have it refreshed)

if I define refresh_store = false, this works and I can fetch passwords from substore by simply adding <store_name>/ as a prefix to path.
It would be cool if either refresh_store would refresh all stores (just like gopass sync does) or it would take an argument with store name and refresh this one.

Expose the entire secret contents?

Just learned a bunch about gopass while debugging my way trying to get a hold of the entire secret contents. A couple of things surprised me as I'm obviously no pass/gopass expert;

  • .password: the first line of the secret
  • .body: remaining lines of the secret

I've got a RSA private key in my pass store that I want to read. That secret contains the whole private key and nothing more, e.g:

-----BEGIN RSA PRIVATE KEY-----

...

-----END RSA PRIVATE KEY-----

I'd really appreciate if I could get a hold of the entire contents in that secret, without having to concatenate password + body everywhere.. Found that gopass exposes this as Secret.String().

Threw a test together locally and that put that into the .body attribute instead and it worked as expected for my use case.

Got any thoughts about exposing that somehow, e.g. a new string attribute on the data source?

[BUGFIX] Return err on ResourceRead failure

#28 Fixes error when gpg file is not found or cannot be decoded. Fixes the erroneous error message detailed below.

If a gpg file is not found in the store, then terraform fails with

* pass_password.my_keyid: pass_password.my_keyid: connection is shut down

terraform plan failed (rc=1). Please fix the error reported and try again.

This occured when a secret is referenced multiple times in terraform resources that are being deleted. The first occurrence deletes the referenced secret from the store and subsequent reads fail.

Perhaps the course of action is not fail silently on deletes and reads in this scenario.

Or, to create a provider config variable to warn on deletes and not delete from the store.

Publish to Terraform Provider Registry

Terraform 0.13 will support to automatically download community provider that are published to the Terraform Provider Registry. It would be great if the pass provider would also be published there.

provider does not work if user updated to gopass 1.10

Gopass 1.10 has changed structure of config file.
This plugin is crashing when a config is from version 1.10.

I guess easiest way would to update gopass dependency here.

To reproduce:
initialize new config with gopass 1.10
try to interact with it

New resource to initialise password store

It would be great to have some kind of resource for initialising password stores, which currently has to be handled outside of terraform:

    pass init [--path=subfolder,-p subfolder] gpg-id...
        Initialize new password storage and use gpg-id for encryption.
        Selectively reencrypt existing passwords using new gpg-id.

Something like this

resource pass_password_store "secret" {
  path = "location of password store"
  gpg-ids = ["ID1", "ID2", "ID3"]

  subfolder {
    path = "subfolder1"
    gpg-ids = ["ID1", "ID2"]
  } 
  subfolder {
    path = "subfolder2"
    gpg-ids = ["ID2", "ID3"]
  }
}

Don't know how well this aligns with the current provider config, but not having to maintain password store access control outside of terraform is kinda painful.

Missing Gopkg.toml

After running the go get ... command and changing into the terraform-provider-passdir, the dep ensurecommand returns:

could not find project Gopkg.toml, use dep init to initiate a manifest

Any solutions?

terraform 0.13 provider changes & terraform registry

Hi,

thanks for the useful terraform provider.

Since terraform 0.13 the mechanism for third-party has been changed.
Furthermore terraform providers can now be pulled from a registry (https://registry.terraform.io/).

So to fix this issue it would be nice to either have the README updated according to the installation of third-party plugins within terraform 0.13 (see https://www.hashicorp.com/blog/automatic-installation-of-third-party-providers-with-terraform-0-13) or even better, push the pass provider into the global terraform registry.

Thanks,
Philipp

FTBFS on FreeBSD arm 6

   ⨯ release failed after 16.37s error=failed to build for freebsd_arm_6: # github.com/gopasspw/gopass/pkg/clipboard
../../../../pkg/mod/github.com/gopasspw/[email protected]/pkg/clipboard/clipboard_others.go:27:6: undefined: killPrecedessors

pass_password with concurrency fails because of git locks.

example:

provider "pass" {
  refresh_store = false
}
resource "pass_password" "a" {
  path = "test/a"
  password = "a"
}
resource "pass_password" "b" {
  path = "test/b"
  password = "b"
}
resource "pass_password" "c" {
  path = "test/c"
  password = "c"
}

results in:

pass_password.b: Creating...
  password: "" => "b"
  path:     "" => "test/b"
pass_password.a: Creating...
  password: "" => "a"
  path:     "" => "test/a"
pass_password.c: Creating...
  password: "" => "c"
  path:     "" => "test/c"
pass_password.a: Creation complete after 4s (ID: test/a)

Error: Error applying plan:

2 error(s) occurred:

* pass_password.c: 1 error(s) occurred:

* pass_password.c: failed to write secret at test/c: failed to commit changes to git: exit status 128
* pass_password.b: 1 error(s) occurred:

* pass_password.b: failed to write secret at test/b: failed to push to git remote: exit status 1
pass_password.b: Creating...
  password: "" => "b"
  path:     "" => "test/b"
pass_password.a: Creating...
  password: "" => "a"
  path:     "" => "test/a"
pass_password.c: Creating...
  password: "" => "c"
  path:     "" => "test/c"
pass_password.a: Creation complete after 4s (ID: test/a)

Error: Error applying plan:

2 error(s) occurred:

* pass_password.c: 1 error(s) occurred:

* pass_password.c: failed to write secret at test/c: failed to commit changes to git: exit status 128
* pass_password.b: 1 error(s) occurred:

* pass_password.b: failed to write secret at test/b: failed to push to git remote: exit status 1

This happens on both create and destroy.
Adding dependency chaining resolves the issue:

provider "pass" {
  refresh_store = false
}
resource "pass_password" "a" {
  path = "test/a"
  password = "a"
}
resource "pass_password" "b" {
  path = "test/b"
  password = "b"
  depends_on = [ "pass_password.a" ]
}
resource "pass_password" "c" {
  path = "test/c"
  password = "c"
  depends_on = [ "pass_password.b" ]
}

But is a inflexible, as this renders the provider overly complex to use in modules.
e.g.

####module-definition
resource "pass_password" "module_a_a" {
  path = "a"
  password "a"
}
resource "pass_password" "module_a_b" {
  path = "b"
  password "b"
  depends_on [ "pass_password.module_a_a" ] 
}
output "last_pass_resource" { 
  value = "${pass_password.module_a_b.path}" # could technically use depends_on, but that has issues too.
}
#### end module definition
module "foo" {
  source = "path-to-module-definition"
}
module "bar" {
  source = "path-to-module-definition"
  dummy_param = "${module.foo.last_pass_resource}" # as modules don't have depends_on
}
module "baz" {
  source = "path-to-module-definition"
  dummy_param = "${module.bar.last_pass_resource}" 
}

Any chance this could be flagged with a semaphore + wait or similar for each operation?
The chances for race conditions are git commit + push, oftentimes giving a 6+ second window for race conditions to cause this issue?

Improving the docs

Hi 👋

I just started using the provider (thanks!). I was looking at opening at PR that adds further documentation for the resources and data sources so it matches the Terraform docs more closely (e.g. listing the arguments and attributes for data sources and resources). This would have made things a little bit quicker for me as I ended up looking into the code to find that info originally.

I'm happy to put in a PR if you think this would be useful?

Thanks

2.0.0 is sorting lines in full attribute

Description

I recently upgraded to 2.0.0 from 1.4.0 which broke my Google provider, because it appears that 2.0.0 is sorting full attribute lines for no good reason.

Reproducing

Here's a test Terraform module:

data "pass_password" "debug" {
  path = "test-json-entry"
}
output "debug" {
  value = data.pass_password.debug.full
}

Where test-json-entry is an entry in pass that consists of:

{
  "B": "b",
  "C": "c",
  "A": "a"
}

With 1.4.0 installed this is the output:

 > terraform output
debug = <<EOT
{
  "B": "b",
  "C": "c",
  "A": "a"
}

EOT

But with 2.0.0 it looks like this:

 > terraform output
debug = <<EOT
{
"A": "a"
"B": "b",
"C": "c",
}

EOT

As seen, the lines are sorted in a way that breaks JSON because of the missing trailing comma.
This was tested on Terraform v0.14.4.

Build fails

I cannot build the provider. I tried it with the instructions from the README and also with

go get github.com/camptocamp/terraform-provider-pass

but I get the following error:

# github.com/camptocamp/terraform-provider-pass/pass
src/github.com/camptocamp/terraform-provider-pass/pass/provider.go:49:19: multiple-value action.New() in single-value context
src/github.com/camptocamp/terraform-provider-pass/pass/provider.go:49:54: cannot use "github.com/blang/semver".Version literal (type "github.com/blang/semver".Version) as type "github.com/justwatchcom/gopass/vendor/github.com/blang/semver".Version in argument to action.New

Any idea how to proceed?

Can't refresh password store when using latest version of gopass

Config file changed in recent version of gopass. path is now in this format:

path: gpgcli-gitcli-fs+file:///home/<foo>/.password-store

The provider fails refreshing the state with:

Error: Error refreshing state: 1 error(s) occurred:                                                                                                                                              
                                                                                                                                                                                                 
* provider.pass: error refreshing password store: failed to run git pull on sub store root: failed to run command /usr/bin/git [git pull]: chdir /home/<foo>/tmp/tf/gpgcli-gitcli-fs+file:/~/
.password-store: no such file or directory

I tried to update dependencies and recompile to see if it fixes this bug, but compilation fails...

@raphink any idea?

Failed to decrypt with gnupg 2.2.4

I don't think this is an issue with this terraform provider, but I opened a ticket here first because it is easily reproducible with a simple terraform config. Mainly wanted to drop it here to see if anyone else has run into this issue.

Given a sufficiently large set of pass secrets to refresh, we get failed to decrypt errors on plans. This only seems to happen on > 2.1 gnupg. We have tried several version of terraform and gnupg, but our testing seems to point to gnupg > 2.1.

Here is some terraform that will replicate the behavior.

provider "pass" {
  refresh_store = false
}

data "pass_password" "dummy" {
  path = "dummy/dummy-${count.index + 1}"

  count = "100"
}

We consisitently get something like this:

Error: Error refreshing state: 1 error(s) occurred:

* data.pass_password.dummy: 23 error(s) occurred:

* data.pass_password.dummy[46]: data.pass_password.dummy.46: failed to read password at dummy/dummy-47: Failed to decrypt
* data.pass_password.dummy[12]: data.pass_password.dummy.12: failed to read password at dummy/dummy-13: Failed to decrypt
<redacted more errors>

Software versions:

gpg (GnuPG) 2.2.4
Terraform v0.11.7
terraform-provider-pass 1.0.1

What is Pass?

I have no idea what software this Terraform module references. Perhaps provide a link to what Pass is in the README?

pass_password resource won't create single-line secrets

If I create a secret only with 'password' specify it still appends the data section to the entry.

resource "pass_password" "mysecret" {
  path     = "secret/foo"
  password = "secret"
}

yields

secret
---
{}

Expected output

secret

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.