• Stars
    star
    116
  • Rank 294,184 (Top 6 %)
  • Language
    Rust
  • License
    Mozilla Public Li...
  • Created about 6 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐ŸŒŒ Pluggable authoritative DNS server. Entries can be added & removed from an HTTP REST API.

Constellation

Test and Build Build and Release dependency status Buy Me A Coffee

Pluggable authoritative DNS server. Entries can be added & removed from an HTTP REST API.

Constellation is a small authoritative server that lets you manage DNS entries from an HTTP REST API, in a generic way. It can be plugged to your existing infrastructure to manage DNS records for users of your service, eg. to configure outbound email records that cannot be easily wildcarded in a traditional DNS server (DKIM, DMARC, SPF records).

DNS entries are stored in Redis. The DNS database can thus be easily modified and dumped for backup purposes.

Tested at Rust version: rustc 1.62.0 (a8314ef7d 2022-06-27)

๐Ÿ‡ซ๐Ÿ‡ท Crafted in Angers, France.

Constellation

Who uses it?

Crisp

๐Ÿ‘‹ You use Constellation and you want to be listed there? Contact me.

Features

  • Pluggable authoritative DNS server, comes handy if you need to generate eg. email sub-domains for your users (with DKIM, DMARC and SPF records).
  • HTTP REST API to check, read, insert, modify and delete DNS records on the fly.
  • Persistence layer in Redis. This means you can run multiple Constellations hitting against the same database on the network. You can even shard Redis if you need fault tolerance on the DNS data store.
  • Geo-DNS to serve records on a location basis. For instance, serve the IP to your US server for all North America users, and fallback to Europe for the rest. Based on MaxMind GeoLite2 free database, that is automatically updated when necessary.

How to use it?

Installation

Constellation is built in Rust. To install it, either download a version from the Constellation releases page, use cargo install or pull the source code from master.

๐Ÿ‘‰ Each release binary comes with an .asc signature file, which can be verified using @valeriansaliou GPG public key: ๐Ÿ”‘valeriansaliou.gpg.pub.asc.

Install from source:

If you pulled the source code from Git, you can build it using cargo:

cargo build --release

You can find the built binaries in the ./target/release directory.

Install libssl-dev (ie. OpenSSL headers) before you compile Constellation. SSL dependencies are required for the Geo-DNS database updater and the DNS health check system (HTTPS prober).

Install from Cargo:

You can install Constellation directly with cargo install:

cargo install constellation-server

Ensure that your $PATH is properly configured to source the Crates binaries, and then run Constellation using the constellation command.

Install from Docker Hub:

You might find it convenient to run Constellation via Docker. You can find the pre-built Constellation image on Docker Hub as valeriansaliou/constellation.

Pre-built Docker version may not be the latest version of Constellation available.

First, pull the valeriansaliou/constellation image:

docker pull valeriansaliou/constellation:v1.14.1

Then, seed it a configuration file and run it (replace /path/to/your/constellation/config.cfg with the path to your configuration file):

docker run -p 53:53/udp -p 8080:8080 -v /path/to/your/constellation/config.cfg:/etc/constellation.cfg -v /path/to/your/constellation/res/:/var/lib/constellation/ valeriansaliou/constellation:v1.14.1

In the configuration file, ensure that:

  • dns.inets is set to [0.0.0.0:53] (this lets Constellation DNS be reached from outside the container)
  • http.inet is set to 0.0.0.0:8080 (this lets Constellation REST API be reached from outside the container)
  • geo.database_path is set to /var/lib/constellation/geo/ (this is where the GeoIP database is stored)

Constellation will be reachable by DNS resolvers from udp://localhost:53; while its internal REST API will be reachable from http://localhost:8080.

Also, do not forget to initialize the GeoIP database in the ./res/geo/ folder (refer to the part on how to Initialize GeoIP below).

Configuration

Use the sample config.cfg configuration file and adjust it to your own environment.

Available configuration options are commented below, with allowed values:

[server]

  • log_level (type: string, allowed: debug, info, warn, error, default: error) โ€” Verbosity of logging, set it to error in production
  • identifier (type: string, allowed: text values, default: constellation/0) โ€” Identifier of this Constellation server in the pool of replicas (used for identification and notification purposes)

[dns]

  • inets (type: array[string], allowed: IPs + ports, default: [0.0.0.0:53, [::]:53]) โ€” Hosts and UDP/TCP ports the DNS server should listen on
  • tcp_timeout (type: integer, allowed: seconds, default: 2) โ€” Timeout of DNS over TCP connections
  • nameservers (type: array[string], allowed: domain names, default: no default) โ€” Name server domains for all served domains
  • soa_master (type: string, allowed: domain names, default: no default) โ€” SOA master domain for all zones served by this name server (name of primary NS server)
  • soa_responsible (type: string, allowed: email addresses as domain names, default: no default) โ€” SOA responsible email for all zones served by this name server
  • soa_refresh (type: integer, allowed: seconds, default: 10000) โ€” SOA record refresh value
  • soa_retry (type: integer, allowed: seconds, default: 2400) โ€” SOA record retry value
  • soa_expire (type: integer, allowed: seconds, default: 604800) โ€” SOA record expire value
  • soa_ttl (type: integer, allowed: seconds, default: 3600) โ€” SOA record TTL value
  • record_ttl (type: integer, allowed: seconds, default: 3600) โ€” DNS records TTL value

[[dns.zone.'{name}']]

Specify your zone name eg. as: [[dns.zone.'relay.crisp.chat']] for zone base: relay.crisp.chat.

[dns.flatten]

  • resolvers (type: array[string], allowed: hostname, IPv4, IPv6, default: no default) โ€” DNS resolvers that should be used when flattening a CNAME record

[dns.health]

  • check_enable (type: boolean, allowed: true, false, default: false) โ€” Whether to perform periodic health checks or not
  • check_interval (type: integer, allowed: seconds, default: 60) โ€” Interval for which to perform health checks in seconds (from 1 minute to 5 minutes is recommended)

[dns.health.notify]

  • slack_hook_url (type: string, allowed: URL, default: no default) โ€” Slack hook URL for notifications (ie. https://hooks.slack.com/[..])

[[dns.health.http]]

  • zone (type: string, allowed: any zone root domain, default: no default) โ€” Root domain for zone to be checked (eg. relay.crisp.chat)
  • name (type: string, allowed: any subdomain on zone, default: no default) โ€” Subdomain for zone to be checked (eg. client.@, for expanded domain client.relay.crisp.chat)
  • method (type: string, allowed: HEAD, GET, default: GET) โ€” HTTP method to be used by HTTP health probe to perform the check request
  • host (type: string, allowed: HTTP virtual hosts, default: empty) โ€” HTTP virtual host to be requested upon check (if not set, it is generated from zone and name)
  • path (type: string, allowed: HTTP paths, default: /) โ€” HTTP path to be requested upon check
  • port (type: integer, allowed: TCP ports, default: 443) โ€” TCP port used for HTTP check (port value will likely be 80 if HTTP is used)
  • secure (type: boolean, allowed: true, false, default: true) โ€” Whether to perform health checks over secure HTTPS or plain HTTP
  • timeout (type: integer, allowed: seconds, default: 10) โ€” Timeout of a single HTTP check attempt in seconds
  • max_attempts (type: integer, allowed: numbers, default: 3) โ€” Maximum number of times to attempt a given health check in a row, in the event of a failed health check (ie. an health check that neither matches expected status and expected body)
  • expected_status (type: array[integer], allowed: HTTP status codes, default: 200) โ€” List of HTTP status codes to expect
  • expected_body (type: array[string], allowed: text values, default: empty) โ€” List of body contents to expect (sub-string can be contained in response body; only applicable if method is set to GET)

[geo]

  • database_path (type: string, allowed: folder path, default: ./res/geo/) โ€” Path to the folder containing the GeoIP database
  • database_file (type: string, allowed: file name, default: GeoLite2-Country.mmdb) โ€” File name for the GeoIP2 MMDB database in the database folder (either free GeoLite2 or paid GeoIP2; enable geo.update_enable if you want to automatically update this file from a remote download server)
  • update_enable (type: boolean, allowed: true, false, default: false) โ€” Whether to enable GeoIP database updater or not
  • update_interval (type: integer, allowed: seconds, default: 864000) โ€” Interval for which to refresh GeoIP database in seconds (1 week or more is recommended)
  • update_url (type: string, allowed: HTTP URL, default: empty) โ€” URL to the compressed GeoIP MMDB file (supported: tar.gz), that is downloaded on refresh (a value is required if geo.update_enable is enabled)

[http]

  • inet (type: string, allowed: IPv4 / IPv6 + port, default: [::1]:8080) โ€” Host and TCP port the HTTP API server should listen on
  • workers (type: integer, allowed: any number, default: 2) โ€” Number of workers for the HTTP API server to run on
  • record_token (type: string, allowed: secret token, default: no default) โ€” Record secret token for management API access (ie. secret password)

[redis]

  • database (type: integer, allowed: 0 to 255, default: 0) โ€” Target Redis database
  • pool_size (type: integer, allowed: 0 to (2^32)-1, default: 8) โ€” Redis connection pool size
  • max_lifetime_seconds (type: integer, allowed: seconds, default: 20) โ€” Maximum lifetime of a connection to Redis (you want it below 5 minutes, as this affects the reconnect delay to Redis if a connection breaks)
  • idle_timeout_seconds (type: integer, allowed: seconds, default: 600) โ€” Timeout of idle/dead pool connections to Redis
  • connection_timeout_seconds (type: integer, allowed: seconds, default: 5) โ€” Timeout in seconds to consider Redis dead and reject DNS and HTTP API queries
  • cache_refresh_seconds (type: integer, allowed: seconds, default: 60) โ€” Time in seconds after which a locally-cached record is refreshed from Redis (this should be kept low)
  • cache_expire_seconds (type: integer, allowed: seconds, default: 600) โ€” Time in seconds after which a locally-cached record expires and should be refreshed from Redis (this should be kept low)

[redis.master]

  • host (type: string, allowed: hostname, IPv4, IPv6, default: localhost) โ€” Target master Redis host
  • port (type: integer, allowed: TCP port, default: 6379) โ€” Target master Redis TCP port
  • password (type: string, allowed: password values, default: none) โ€” Master Redis password (if no password, do not set this key)

[[redis.rescue]]

  • host (type: string, allowed: hostname, IPv4, IPv6, default: localhost) โ€” Read-only rescue Redis host
  • port (type: integer, allowed: TCP port, default: 6379) โ€” Read-only rescue Redis TCP port
  • password (type: string, allowed: password values, default: none) โ€” Read-only rescue Redis password (if no password, do not set this key)

Initialize GeoIP

As Constellation does not distribute a GeoIP database in its repository, you will need to fetch it from MaxMind before you run Constellation for the first time (Constellation will refuse to start otherwise).

Execute the provided script:

./scripts/init_geoip.sh --license_key=YOUR_GEOLITE2_LICENSE_KEY

YOUR_GEOLITE2_LICENSE_KEY should be replaced by a valid GeoLite2 license key. Please follow instructions provided by MaxMind to obtain a license key.

Note that once Constellation started from the GeoIP database you have manually initialized, it will keep the database up-to-date by checking and applying updates automatically in the background. The database initialization is a one-time operation. Make sure your license key is also set in the GeoIP update URL in the configuration.

Run Constellation

Constellation can be run as such:

./constellation -c /path/to/config.cfg

Test Constellation

Once running, DNS queries can be made against Constellation over the local network (using the default configuration):

dig subdomain.relay.crisp.chat @::1

Note that the dig utility can be pointed to a specific server with the @ modifier, here with IPv6 localhost: ::1.

๐Ÿ›ฐ HTTP REST API

The Constellation HTTP REST API listens on the configured http.inet interface from your config.cfg file. You can use it for your management and monitoring needs.

If you want to play with the API the easy way, an up-to-date Paw file is available with all API routes and example requests. Download the Paw app for your Mac there (Paw a tool developers use to test their APIs).

1. DNS records management

To check, read, insert, modify and delete DNS records, you can use the zone API resource.

API overview

Endpoint URL:

HTTP http://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

Where:

  • zone_name: The zone name (ie. base domain), eg. relay.crisp.chat
  • record_name: The record name to read or alter (ie. sub-domain or base domain), eg. client.@ for the client.relay.crisp.chat FQDN, or @ for the relay.crisp.chat FQDN
  • record_type: The DNS record type to read or alter for the record_name; either: a, aaaa, cname, mx, txt or ptr (open an issue if you need support for another record type)

Request headers:

  • Add an Authorization header with a Basic authentication where the password is your configured http.record_token.

Geo-DNS regions:

If you want to serve records to the nearest server using the Geo-DNS feature, you will need to set regions via the API, where:

  • Americas

    • nnam: Northern North America
    • snam: Southern North America
    • nsam: Northern South America
    • ssam: Southern South America
  • Europe

    • weu: Western Europe
    • ceu: Central Europe
    • eeu: Eastern Europe
    • ru: Russia
  • Middle East

    • me: Middle East
  • Africa

    • naf: Northern Africa
    • maf: Middle Africa
    • saf: Southern Africa
  • Asia

    • in: India
    • seas: Southeast Asia
    • neas: Northeast Asia
  • Oceania

    • oc: Oceania

Geo-DNS blackhole:

If you want to return an empty DNS response for blocked countries using the Geo-DNS feature, you will need to set blackhole via the API, to a list of blackholed ISO-3166 Alpha-2 country codes (eg. FR for France).

Rescue records for health-check:

In case you are using health-check on the domain for zone, you may want to specify rescue records, that are served to DNS clients in the event all regular records (standard and Geo-DNS) are seen as dead. You can set the rescue property in the API to ensure failover servers are served, and connected to only in the event of a failure of default servers.

If you do not set any rescue records; in the event all regular records get reported as dead, DNS clients will be served an empty response. Thus, it is judicious that you still serve fallback records.

CNAME flattening:

CNAMEs are handy to centralize record values in a single DNS entry, and re-use it across multiple DNS CNAME entries. It has its caveats, as for instance, it is illegal as per the DNS RFC to share it with other records on the same sub-domain. It is also illegal to setup a CNAME at the root of a domain. Furthermore, CNAMEs require DNS resolvers to perform a second resolving step as to resolve the flat value (eg. A, AAAA, TXT, etc. records), which is not super efficient as it adds extraneous latency when users resolve a domain using a CNAME.

CNAME flattening can help if you encounter an edge case of the DNS RFC with a CNAME record type. It lets Constellation resolve the actual flat value, and serve it right away, instead of returning the actual CNAME. CNAME flattening can be enabled for a record by setting the flatten property in the API to true. By default, no CNAME flattening is performed.

A dedicated Constellation thread manages previously-flattened CNAME values, and updates them as they change on their remote DNS server. As well, if a cached flattened CNAME has not been used for a long time, it is expunged from cache. Note that, due to the fact that Constellation is mono-threaded, if a CNAME value with flattening enabled is not yet in cache, then Constellation will answer with the CNAME back, and delegate a deferred flatten order to the flattening manager thread, in order to avoid blocking the main DNS server thread. Once the flattening manager thread has done its work, further DNS queries to the CNAME will then be answered with the flattened value (eg. it will return flat A record values, instead of the CNAME value).

Note that the flatten option is only applicable to records with CNAME values. If flattening is enabled on eg. a A record type, the flatten property will have no effect.

API routes

Check if a DNS record exists

HTTP HEAD http://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

Example request:

HEAD /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

Example response:

HTTP/1.1 200 OK
Get a DNS record

HTTP GET http://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

Example request:

GET /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{"type":"a","name":"@","ttl":600,"blackhole": null,"regions": null,"values":["159.89.97.13","46.101.18.133"]}
Write a DNS record (or overwrite existing)

HTTP PUT http://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

Example request (standard):

PUT /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["159.89.97.13","46.101.18.133"],"ttl":600}

Example request (Geo-DNS):

PUT /zone/relay.crisp.chat/record/@/cname HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"regions":{"nnam":["client.nnam.geo.relay.crisp.net"],"snam":["client.snam.geo.relay.crisp.net"],"nsam":["client.nsam.geo.relay.crisp.net"],"ssam":["client.ssam.geo.relay.crisp.net"],"weu":["client.weu.geo.relay.crisp.net"],"ceu":["client.ceu.geo.relay.crisp.net"],"eeu":["client.eeu.geo.relay.crisp.net"],"ru":["client.ru.geo.relay.crisp.net"],"me":["client.me.geo.relay.crisp.net"],"naf":["client.naf.geo.relay.crisp.net"],"maf":["client.maf.geo.relay.crisp.net"],"saf":["client.saf.geo.relay.crisp.net"],"in":["client.in.geo.relay.crisp.net"],"seas":["client.seas.geo.relay.crisp.net"],"neas":["client.neas.geo.relay.crisp.net"],"oc":["client.oc.geo.relay.crisp.net"]},"values":["client.default.geo.relay.crisp.net"],"ttl":600}

Example request (health-checked):

PUT /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["159.89.97.13","46.101.18.133"],"rescue":["139.59.174.13"],"ttl":60}

Example response:

HTTP/1.1 200 OK

Example request (CNAME-flattened):

PUT /zone/relay.crisp.chat/record/@/cname HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==
Content-Type: application/json; charset=utf-8

{"values":["alias.crisp.net"],"flatten":true,"ttl":60}

Example response:

HTTP/1.1 200 OK
Delete a DNS record

HTTP DELETE http://constellation.local:8080/zone/<zone_name>/record/<record_name>/<record_type>/

Example request:

DELETE /zone/relay.crisp.chat/record/@/a HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

Example response:

HTTP/1.1 200 OK

2. Server usage metrics retrieval

To obtain server usage metrics (eg. which countries DNS requests come), you can use the metrics API resource.

API overview

Endpoint URL:

HTTP http://constellation.local:8080/zone/<zone_name>/metrics/<metrics_timespan>/<metrics_category>/<metrics_type>

Where:

  • zone_name: The zone name (ie. base domain), eg. relay.crisp.chat
  • metrics_timespan: The timespan over which metrics should be returned (either: 1m, 5m or 15m), which stands for: metrics for the last 'n-th' minutes
  • metrics_category: The metrics category (either: query or answer)
  • metrics_type: The metrics type in category (either: types or origins if category is query, or codes if category is answer)

Request headers:

  • Add an Authorization header with a Basic authentication where the password is your configured http.record_token.

API routes

Get metrics

HTTP GET http://constellation.local:8080/zone/<zone_name>/metrics/<metrics_timespan>/<metrics_category>/<metrics_type>/

Example request:

GET /zone/relay.crisp.chat/metrics/5m/query/origins HTTP/1.1
Authorization: Basic OlJFUExBQ0VfVEhJU19XSVRIX0FfU0VDUkVUX0tFWQ==

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{"fr":1203,"us":899,"lv":23,"gb":10,"other":2}

๐Ÿ”ฅ Report A Vulnerability

If you find a vulnerability in Constellation, you are more than welcome to report it directly to @valeriansaliou by sending an encrypted email to [email protected]. Do not report vulnerabilities in public GitHub issues, as they may be exploited by malicious people to target production servers running an unpatched Constellation instance.

โš ๏ธ You must encrypt your email using @valeriansaliou GPG public key: ๐Ÿ”‘valeriansaliou.gpg.pub.asc.

More Repositories

1

sonic

๐Ÿฆ” Fast, lightweight & schema-less search backend. An alternative to Elasticsearch that runs on a few MBs of RAM.
Rust
19,483
star
2

vigil

๐Ÿšฆ Microservices Status Page. Monitors a distributed infrastructure and sends alerts (Slack, SMS, etc.).
Rust
1,623
star
3

bloom

๐ŸŒธ HTTP REST API caching middleware, to be used between load balancers and REST API workers.
Rust
704
star
4

node-sales-tax

๐Ÿ’ฐ International sales tax calculator for Node (offline, but provides optional online VAT number fraud check). Tax rates are kept up-to-date.
JavaScript
288
star
5

node-sonic-channel

๐Ÿฆ‰ Sonic Channel integration for Node. Used in pair with Sonic, the fast, lightweight and schema-less search backend.
JavaScript
144
star
6

raider

๐ŸŽ Affiliates dashboard. Used by affiliates to generate tracking codes and review their balance.
Rust
127
star
7

node-fast-ratelimit

โ˜” Fast and efficient in-memory rate-limit for Node, used to alleviate most common DOS attacks.
JavaScript
106
star
8

jquery.clipboard

โœ‚๏ธ jQuery Clipboard plugin (newest version) - Copy any text to the user's clipboard. Implements ZeroClipboard over the jQuery plugin layer.
JavaScript
70
star
9

giggle

๐Ÿ“ž Giggle Jingle library for XMPP, implementation of XEP-0166.
JavaScript
57
star
10

vigil-local

๐Ÿ•ฏ Vigil Local daemon. Used as a slave service to monitor hosts behind a firewall and report their status to Vigil.
Rust
20
star
11

django-gitlab-logging

๐Ÿท A logging handler for Django that opens GitLab issues on server error.
Python
13
star
12

plotters-conrod

๐Ÿ“ˆ Conrod backend for Plotters. This is more efficient than using the default Bitmap backend when plotting in Conrod.
Rust
13
star
13

boulder-dash

:godmode: Boulder Dash game remake, done in Java.
Java
13
star
14

callisto

๐Ÿ’ซ Yet another Solar System simulator, written in Go.
Go
11
star
15

go-vigil-reporter

๐Ÿšง Vigil Reporter for Golang. Used in pair with Vigil, the Microservices Status Page.
Go
11
star
16

node-vigil-reporter

๐Ÿšง Vigil Reporter for Node. Used in pair with Vigil, the Microservices Status Page.
JavaScript
10
star
17

progressio

๐ŸŽˆ Beautiful & stylish asynchronous page loader. Makes a static website dynamic in a breeze.
JavaScript
7
star
18

gulp-remove-logging

๐Ÿšฟ Removes console logging statements.
JavaScript
7
star
19

node-gitlab-logging

๐Ÿบ A logging handler for NodeJS that opens GitLab issues on provided exception.
JavaScript
7
star
20

node-bloom-control

๐Ÿ’ Bloom Control integration for Node. Used in pair with Bloom, the HTTP REST API caching middleware.
JavaScript
7
star
21

rs-vigil-reporter

๐Ÿšง Vigil Reporter for Rust. Used in pair with Vigil, the Microservices Status Page.
Rust
6
star
22

grunt-blurred-images

๐Ÿ”ฎ Produce blurred versions of images. Used to reproduce Medium blur-on-scroll effect.
JavaScript
5
star
23

django-request-mock

๐Ÿ™ˆ Create a Django request object that mocks a real one. Useful in case a real request object is not available, but is needed (delayed Celery tasks for instance)
Python
5
star
24

waaave-bootstrap

๐Ÿ„ The Waaave Bootstrap. Efficient by design.
JavaScript
4
star
25

waaave-web

๐Ÿ„ Waaave, The Developer Sharing Network. Tutorials, Shots and more.
Python
4
star
26

grunt-contrib-lualint

๐Ÿ’Š Grunt task for validating Lua code.
Lua
2
star
27

server-workflow-scripts

๐Ÿน Server workflow scripts for fast project deployment and execution - used with valeriansaliou/gitlab-deploy-hooks
Shell
2
star
28

datastore.js

๐Ÿ˜ A complete Web storage wrapper (sessionStorage/localStorage). Provides a fallback when not supported.
JavaScript
2
star
29

jquery.hasparent

๐Ÿ’ก jQuery hasParent helper. Checks if the selected element has a defined parent element.
JavaScript
2
star
30

medius

๐ŸŠ Learn how the immune system works with Medius, a real-time fight game.
Python
2
star
31

lab-iot-homekit

๐Ÿ’ก HomeKit-powered home automation IoT projects, running on ESP32.
C
2
star
32

gulp-jade-client

๐Ÿฐ Compiles Jade templates from the browser.
JavaScript
1
star
33

waaave-hitcount

๐Ÿ„ Basic app that allows you to track the number of hits/views for a particular object.
Python
1
star
34

valeriansaliou

๐Ÿ‘ฑ๐Ÿปโ€โ™‚๏ธ My GitHub profile.
1
star
35

backlinks-manager

๐ŸŽƒ BackLinks.com ads manager. Easily deploy a BackLinks.com ad code, with a fast cache system.
PHP
1
star
36

lab-eigenfaces

๐Ÿ’ก Face recognition algorithm implementation, using the eigenfaces technique.
MATLAB
1
star
37

grunt-contrib-rubylint

๐Ÿ’Š Grunt task for validating Ruby code.
JavaScript
1
star
38

dns-deploy-utilities

๐Ÿฌ Utilities to deploy DNS configurations for the BIND9 nameserver.
1
star