Compare commits
5 Commits
6d68b4e5ab
...
e5ce19e850
Author | SHA1 | Date | |
---|---|---|---|
|
e5ce19e850 | ||
|
7db28b3793 | ||
|
e9dc6ba090 | ||
|
ef2c179a68 | ||
|
6c32af061b |
2
.env.dev
2
.env.dev
@ -1,5 +1,5 @@
|
|||||||
export DOMIPLY_HTTP_DOMAIN=localhost
|
export DOMIPLY_HTTP_DOMAIN=localhost
|
||||||
export DOMIPLY_PASSPHRASE=foobar
|
export DOMIPLY_PASSPHRASE=foobar
|
||||||
export DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH=/tmp/domiply_dev_env/origin/git
|
export DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH=/tmp/domiply_dev_env/origin/git
|
||||||
export DOMIPLY_DOMAIN_CHECKER_TARGET_AAAA=::1
|
export DOMIPLY_DOMAIN_CHECKER_TARGET_A=127.0.0.1
|
||||||
export DOMIPLY_DOMAIN_CONFIG_STORE_DIR_PATH=/tmp/domiply_dev_env/domain/config
|
export DOMIPLY_DOMAIN_CONFIG_STORE_DIR_PATH=/tmp/domiply_dev_env/domain/config
|
||||||
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Domiply
|
||||||
|
|
||||||
|
Domiply is a self-hosted rust service which connects a DNS hostname to a data
|
||||||
|
backend (e.g. a git repository), all with no account needed. The user only
|
||||||
|
inputs their domain name, their desired backend, and then adds two entries to
|
||||||
|
their DNS server.
|
||||||
|
|
||||||
|
[Demo which may or may not be live](https://domiply.mediocregopher.com)
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Domiply uses nix flakes for building and setting up the development environment.
|
||||||
|
In order to open a shell with all necessary tooling (expected rust toolchain
|
||||||
|
versions, etc...) simply do:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix develop
|
||||||
|
```
|
||||||
|
|
||||||
|
Within the shell which opens you can do `cargo run` to start a local instance.
|
||||||
|
|
||||||
|
In order to create a release binary:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
Check out the `src/service/http_tpl/index.html` file for the current roadmap.
|
@ -20,8 +20,8 @@ pub enum NewDNSCheckerError {
|
|||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum CheckDomainError {
|
pub enum CheckDomainError {
|
||||||
#[error("target AAAA not set")]
|
#[error("target A not set")]
|
||||||
TargetAAAANotSet,
|
TargetANotSet,
|
||||||
|
|
||||||
#[error("challenge token not set")]
|
#[error("challenge token not set")]
|
||||||
ChallengeTokenNotSet,
|
ChallengeTokenNotSet,
|
||||||
@ -31,7 +31,7 @@ pub enum CheckDomainError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct DNSChecker {
|
pub struct DNSChecker {
|
||||||
target_aaaa: net::Ipv6Addr,
|
target_a: net::Ipv4Addr,
|
||||||
|
|
||||||
// TODO we should use some kind of connection pool here, I suppose
|
// TODO we should use some kind of connection pool here, I suppose
|
||||||
client: tokio::sync::Mutex<AsyncClient>,
|
client: tokio::sync::Mutex<AsyncClient>,
|
||||||
@ -39,7 +39,7 @@ pub struct DNSChecker {
|
|||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
tokio_runtime: sync::Arc<tokio::runtime::Runtime>,
|
tokio_runtime: sync::Arc<tokio::runtime::Runtime>,
|
||||||
target_aaaa: net::Ipv6Addr,
|
target_a: net::Ipv4Addr,
|
||||||
resolver_addr: &str,
|
resolver_addr: &str,
|
||||||
) -> Result<DNSChecker, NewDNSCheckerError> {
|
) -> Result<DNSChecker, NewDNSCheckerError> {
|
||||||
let resolver_addr = resolver_addr
|
let resolver_addr = resolver_addr
|
||||||
@ -55,7 +55,7 @@ pub fn new(
|
|||||||
tokio_runtime.spawn(bg);
|
tokio_runtime.spawn(bg);
|
||||||
|
|
||||||
Ok(DNSChecker {
|
Ok(DNSChecker {
|
||||||
target_aaaa,
|
target_a,
|
||||||
client: tokio::sync::Mutex::new(client),
|
client: tokio::sync::Mutex::new(client),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -84,14 +84,14 @@ impl DNSChecker {
|
|||||||
let records = response.answers();
|
let records = response.answers();
|
||||||
|
|
||||||
if records.len() != 1 {
|
if records.len() != 1 {
|
||||||
return Err(CheckDomainError::TargetAAAANotSet);
|
return Err(CheckDomainError::TargetANotSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the single record isn't a AAAA, or it's not the target AAAA, then return
|
// if the single record isn't a A, or it's not the target A, then return
|
||||||
// TargetAAAANAMENotSet
|
// TargetANAMENotSet
|
||||||
match records[0].data() {
|
match records[0].data() {
|
||||||
Some(RData::AAAA(remote_aaaa)) if remote_aaaa == &self.target_aaaa => (),
|
Some(RData::A(remote_a)) if remote_a == &self.target_a => (),
|
||||||
_ => return Err(CheckDomainError::TargetAAAANotSet),
|
_ => return Err(CheckDomainError::TargetANotSet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ pub enum SyncWithConfigError {
|
|||||||
#[error("already in progress")]
|
#[error("already in progress")]
|
||||||
AlreadyInProgress,
|
AlreadyInProgress,
|
||||||
|
|
||||||
#[error("target AAAA not set")]
|
#[error("target A/AAAA not set")]
|
||||||
TargetAAAANotSet,
|
TargetANotSet,
|
||||||
|
|
||||||
#[error("challenge token not set")]
|
#[error("challenge token not set")]
|
||||||
ChallengeTokenNotSet,
|
ChallengeTokenNotSet,
|
||||||
@ -96,7 +96,7 @@ impl From<origin::store::SyncError> for SyncWithConfigError {
|
|||||||
impl From<checker::CheckDomainError> for SyncWithConfigError {
|
impl From<checker::CheckDomainError> for SyncWithConfigError {
|
||||||
fn from(e: checker::CheckDomainError) -> SyncWithConfigError {
|
fn from(e: checker::CheckDomainError) -> SyncWithConfigError {
|
||||||
match e {
|
match e {
|
||||||
checker::CheckDomainError::TargetAAAANotSet => SyncWithConfigError::TargetAAAANotSet,
|
checker::CheckDomainError::TargetANotSet => SyncWithConfigError::TargetANotSet,
|
||||||
checker::CheckDomainError::ChallengeTokenNotSet => {
|
checker::CheckDomainError::ChallengeTokenNotSet => {
|
||||||
SyncWithConfigError::ChallengeTokenNotSet
|
SyncWithConfigError::ChallengeTokenNotSet
|
||||||
}
|
}
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -29,8 +29,8 @@ struct Cli {
|
|||||||
#[arg(long, required = true, env = "DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH")]
|
#[arg(long, required = true, env = "DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH")]
|
||||||
origin_store_git_dir_path: path::PathBuf,
|
origin_store_git_dir_path: path::PathBuf,
|
||||||
|
|
||||||
#[arg(long, required = true, env = "DOMIPLY_DOMAIN_CHECKER_TARGET_AAAA")]
|
#[arg(long, required = true, env = "DOMIPLY_DOMAIN_CHECKER_TARGET_A")]
|
||||||
domain_checker_target_aaaa: std::net::Ipv6Addr,
|
domain_checker_target_a: std::net::Ipv4Addr,
|
||||||
|
|
||||||
#[arg(long, default_value_t = String::from("1.1.1.1:53"), env = "DOMIPLY_DOMAIN_CHECKER_RESOLVER_ADDR")]
|
#[arg(long, default_value_t = String::from("1.1.1.1:53"), env = "DOMIPLY_DOMAIN_CHECKER_RESOLVER_ADDR")]
|
||||||
domain_checker_resolver_addr: String,
|
domain_checker_resolver_addr: String,
|
||||||
@ -115,7 +115,7 @@ fn main() {
|
|||||||
|
|
||||||
let domain_checker = domiply::domain::checker::new(
|
let domain_checker = domiply::domain::checker::new(
|
||||||
tokio_runtime.clone(),
|
tokio_runtime.clone(),
|
||||||
config.domain_checker_target_aaaa,
|
config.domain_checker_target_a,
|
||||||
&config.domain_checker_resolver_addr,
|
&config.domain_checker_resolver_addr,
|
||||||
)
|
)
|
||||||
.expect("domain checker initialized");
|
.expect("domain checker initialized");
|
||||||
@ -128,9 +128,9 @@ fn main() {
|
|||||||
|
|
||||||
let service = domiply::service::new(
|
let service = domiply::service::new(
|
||||||
manager,
|
manager,
|
||||||
config.domain_checker_target_aaaa,
|
config.domain_checker_target_a,
|
||||||
config.passphrase,
|
config.passphrase,
|
||||||
config.http_domain,
|
config.http_domain.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let service = sync::Arc::new(service);
|
let service = sync::Arc::new(service);
|
||||||
@ -153,7 +153,7 @@ fn main() {
|
|||||||
tokio_runtime.spawn(async move {
|
tokio_runtime.spawn(async move {
|
||||||
let addr = config.http_listen_addr;
|
let addr = config.http_listen_addr;
|
||||||
|
|
||||||
println!("Listening on {addr}");
|
println!("Listening on http://{}:{}", config.http_domain, addr.port());
|
||||||
let server = hyper::Server::bind(&addr).serve(make_service);
|
let server = hyper::Server::bind(&addr).serve(make_service);
|
||||||
|
|
||||||
let graceful = server.with_graceful_shutdown(async {
|
let graceful = server.with_graceful_shutdown(async {
|
||||||
|
@ -17,7 +17,7 @@ type SvcResponse = Result<Response<hyper::body::Body>, String>;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Service<'svc> {
|
pub struct Service<'svc> {
|
||||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||||
target_aaaa: net::Ipv6Addr,
|
target_a: net::Ipv4Addr,
|
||||||
passphrase: String,
|
passphrase: String,
|
||||||
http_domain: String,
|
http_domain: String,
|
||||||
handlebars: handlebars::Handlebars<'svc>,
|
handlebars: handlebars::Handlebars<'svc>,
|
||||||
@ -25,13 +25,13 @@ pub struct Service<'svc> {
|
|||||||
|
|
||||||
pub fn new<'svc, 'mgr>(
|
pub fn new<'svc, 'mgr>(
|
||||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||||
target_aaaa: net::Ipv6Addr,
|
target_a: net::Ipv4Addr,
|
||||||
passphrase: String,
|
passphrase: String,
|
||||||
http_domain: String,
|
http_domain: String,
|
||||||
) -> Service<'svc> {
|
) -> Service<'svc> {
|
||||||
Service {
|
Service {
|
||||||
domain_manager,
|
domain_manager,
|
||||||
target_aaaa,
|
target_a,
|
||||||
passphrase,
|
passphrase,
|
||||||
http_domain,
|
http_domain,
|
||||||
handlebars: self::http_tpl::get().expect("Retrieved Handlebars templates"),
|
handlebars: self::http_tpl::get().expect("Retrieved Handlebars templates"),
|
||||||
@ -52,12 +52,12 @@ struct DomainGetArgs {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DomainInitArgs {
|
struct DomainInitArgs {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
passphrase: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct DomainSyncArgs {
|
struct DomainSyncArgs {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
|
passphrase: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'svc> Service<'svc> {
|
impl<'svc> Service<'svc> {
|
||||||
@ -179,31 +179,30 @@ impl<'svc> Service<'svc> {
|
|||||||
config: Option<domain::config::Config>,
|
config: Option<domain::config::Config>,
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.domain_manager.get_config(&args.domain) {
|
let config = match self.domain_manager.get_config(&args.domain) {
|
||||||
Ok(_config) => self.render_error_page(500, "TODO not yet implemented"),
|
Ok(config) => Some(config),
|
||||||
Err(domain::manager::GetConfigError::NotFound) => self.render_page(
|
Err(domain::manager::GetConfigError::NotFound) => None,
|
||||||
|
Err(domain::manager::GetConfigError::Unexpected(e)) => {
|
||||||
|
return self
|
||||||
|
.render_error_page(500, format!("retrieving configuration: {}", e).as_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.render_page(
|
||||||
"/domain.html",
|
"/domain.html",
|
||||||
&Response {
|
&Response {
|
||||||
domain: args.domain,
|
domain: args.domain,
|
||||||
config: None,
|
config: config,
|
||||||
},
|
},
|
||||||
),
|
)
|
||||||
Err(domain::manager::GetConfigError::Unexpected(e)) => {
|
|
||||||
self.render_error_page(500, format!("retrieving configuration: {}", e).as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_init(&self, args: DomainInitArgs, domain_config: util::FlatConfig) -> SvcResponse {
|
fn domain_init(&self, args: DomainInitArgs, domain_config: util::FlatConfig) -> SvcResponse {
|
||||||
if args.passphrase != self.passphrase.as_str() {
|
|
||||||
return self.render_error_page(401, "Incorrect passphrase");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Response {
|
struct Response {
|
||||||
domain: domain::Name,
|
domain: domain::Name,
|
||||||
flat_config: util::FlatConfig,
|
flat_config: util::FlatConfig,
|
||||||
target_aaaa: net::Ipv6Addr,
|
target_a: net::Ipv4Addr,
|
||||||
challenge_token: String,
|
challenge_token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +227,7 @@ impl<'svc> Service<'svc> {
|
|||||||
&Response {
|
&Response {
|
||||||
domain: args.domain,
|
domain: args.domain,
|
||||||
flat_config: config.into(),
|
flat_config: config.into(),
|
||||||
target_aaaa: self.target_aaaa,
|
target_a: self.target_a,
|
||||||
challenge_token: config_hash,
|
challenge_token: config_hash,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -239,6 +238,10 @@ impl<'svc> Service<'svc> {
|
|||||||
args: DomainSyncArgs,
|
args: DomainSyncArgs,
|
||||||
domain_config: util::FlatConfig,
|
domain_config: util::FlatConfig,
|
||||||
) -> SvcResponse {
|
) -> SvcResponse {
|
||||||
|
if args.passphrase != self.passphrase.as_str() {
|
||||||
|
return self.render_error_page(401, "Incorrect passphrase");
|
||||||
|
}
|
||||||
|
|
||||||
let config: domain::config::Config = match domain_config.try_into() {
|
let config: domain::config::Config = match domain_config.try_into() {
|
||||||
Ok(Some(config)) => config,
|
Ok(Some(config)) => config,
|
||||||
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
Ok(None) => return self.render_error_page(400, "domain config is required"),
|
||||||
@ -263,7 +266,7 @@ impl<'svc> Service<'svc> {
|
|||||||
Err(domain::manager::SyncWithConfigError::InvalidURL) => Some("Fetching the git repository failed, please double check that you input the correct URL.".to_string()),
|
Err(domain::manager::SyncWithConfigError::InvalidURL) => Some("Fetching the git repository failed, please double check that you input the correct URL.".to_string()),
|
||||||
Err(domain::manager::SyncWithConfigError::InvalidBranchName) => Some("The git repository does not have a branch of the given name, please double check that you input the correct name.".to_string()),
|
Err(domain::manager::SyncWithConfigError::InvalidBranchName) => Some("The git repository does not have a branch of the given name, please double check that you input the correct name.".to_string()),
|
||||||
Err(domain::manager::SyncWithConfigError::AlreadyInProgress) => Some("The configuration of your domain is still in progress, please refresh in a few minutes.".to_string()),
|
Err(domain::manager::SyncWithConfigError::AlreadyInProgress) => Some("The configuration of your domain is still in progress, please refresh in a few minutes.".to_string()),
|
||||||
Err(domain::manager::SyncWithConfigError::TargetAAAANotSet) => Some("The AAAA record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()),
|
Err(domain::manager::SyncWithConfigError::TargetANotSet) => Some("The A record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()),
|
||||||
Err(domain::manager::SyncWithConfigError::ChallengeTokenNotSet) => Some("The TXT record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()),
|
Err(domain::manager::SyncWithConfigError::ChallengeTokenNotSet) => Some("The TXT record is not set correctly on the domain. Please double check that you put the correct value on the record. If the value is correct, then most likely the updated records have not yet propagated. In this case you can refresh in a few minutes to try again.".to_string()),
|
||||||
Err(domain::manager::SyncWithConfigError::Unexpected(e)) => Some(format!("An unexpected error occurred: {e}")),
|
Err(domain::manager::SyncWithConfigError::Unexpected(e)) => Some(format!("An unexpected error occurred: {e}")),
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/bamboo.css" />
|
<link rel="stylesheet" type="text/css" href="/static/bamboo.css" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
form {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -2,11 +2,21 @@
|
|||||||
Configure New Domain
|
Configure New Domain
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
{{# if data.config }}
|
||||||
|
|
||||||
|
<p>Your domain <code>{{ data.domain }}</code> is already configured with
|
||||||
|
Domiply. You can see the existing configuration below. If you modify any values
|
||||||
|
you will need to hit the "Next" button to complete the update.</p>
|
||||||
|
|
||||||
|
{{ else }}
|
||||||
|
|
||||||
<p>Your domain <code>{{ data.domain }}</code> is not yet configured with Domiply.
|
<p>Your domain <code>{{ data.domain }}</code> is not yet configured with Domiply.
|
||||||
To get started, please input the details of a public git repo which will be used
|
To get started, please input the details of a public git repo which will be used
|
||||||
to serve your domain. When you update the given branch, your domain will be
|
to serve your domain. When you update the given branch, your domain will be
|
||||||
automatically updated too!</p>
|
automatically updated too!</p>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<p><em>In the future Domiply will support more backends than just git
|
<p><em>In the future Domiply will support more backends than just git
|
||||||
repos.</em></p>
|
repos.</em></p>
|
||||||
|
|
||||||
@ -14,11 +24,6 @@ automatically updated too!</p>
|
|||||||
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
<input name="domain" type="hidden" value="{{ data.domain }}" />
|
||||||
<input name="config_origin_descr_kind" type="hidden" value="git" />
|
<input name="config_origin_descr_kind" type="hidden" value="git" />
|
||||||
|
|
||||||
<label>
|
|
||||||
Passphrase (required during closed-beta):
|
|
||||||
<input name="passphrase" placeholder="shhhh" required />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Git Repository</legend>
|
<legend>Git Repository</legend>
|
||||||
<p>
|
<p>
|
||||||
|
@ -5,8 +5,8 @@ are two entries you will need to add:</p>
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
A <code>AAAA {{ data.domain }}</code> entry with the value
|
A <code>A {{ data.domain }}</code> entry with the value
|
||||||
<code>{{ data.target_aaaa }}</code>
|
<code>{{ data.target_a }}</code>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
A <code>TXT _domiply_challenge.{{ data.domain }}</code> entry with the value
|
A <code>TXT _domiply_challenge.{{ data.domain }}</code> entry with the value
|
||||||
@ -22,5 +22,13 @@ and set up your domain.</p>
|
|||||||
{{ #each data.flat_config }}
|
{{ #each data.flat_config }}
|
||||||
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
<input name="{{ @key }}" type="hidden" value="{{ this }}" />
|
||||||
{{ /each }}
|
{{ /each }}
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
Passphrase (required during closed-beta):
|
||||||
|
<input name="passphrase" placeholder="shhhh" required />
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<input type="submit" value="Next" />
|
<input type="submit" value="Next" />
|
||||||
</form>
|
</form>
|
||||||
|
@ -2,41 +2,36 @@
|
|||||||
account needed. Just input your desired backend, add two entries to your DNS
|
account needed. Just input your desired backend, add two entries to your DNS
|
||||||
server, and you're done!</p>
|
server, and you're done!</p>
|
||||||
|
|
||||||
<p><strong>Domiply is currently only a proof-of-concept with limited features,
|
<p><strong>YOU SHOULD NOT USE THIS FOR ANYTHING YOU CARE ABOUT AT THIS
|
||||||
but will continue to be expanded as development time permits</strong></p>
|
TIME.</strong></p>
|
||||||
|
|
||||||
<p>The following backends are supported for serving a domain:</p>
|
<p>Domiply is currently only a proof-of-concept with limited features,
|
||||||
|
but will continue to be expanded as development time permits.</p>
|
||||||
<ul>
|
|
||||||
<li><strong>Git repository</strong>: Your domain will be served out of a
|
|
||||||
branch of a public git repository, a la Github Pages.</li>
|
|
||||||
<li><strong>IPFS/IPNS Hash</strong>: TODO</li>
|
|
||||||
<li><strong>Alternative URL (reverse proxy)</strong>: TODO</li>
|
|
||||||
<li><strong>Google Drive</strong>: TODO (maybe)</li>
|
|
||||||
<li><strong>Dropbox</strong>: TODO (maybe)</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Get Started</h2>
|
<h2>Get Started</h2>
|
||||||
|
|
||||||
<p>Input your domain name below to set it up, or reconfigure it if you have used
|
<p>Input your domain name below to set it up, or to reconfigure it has already
|
||||||
it before:</p>
|
been set up.</p>
|
||||||
|
|
||||||
<form method="get" action="/domain.html">
|
<form method="get" action="/domain.html">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
<label>
|
<label>
|
||||||
Domain name:
|
Domain name:
|
||||||
<input type="text" name="domain" placeholder="example.org" required />
|
<input type="text" name="domain" placeholder="example.org" required />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<input type="submit" value="Go!" />
|
<input type="submit" value="Go!" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p>Alternatively you can do any of the following alternative actions:</p>
|
<p>Alternatively you can do any of the following alternative actions:</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>List all existing domains</li>
|
<li>List all existing domains (TODO)</li>
|
||||||
<li>View the Source Code (TODO)</li>
|
<li>View the Source Code (TODO)</li>
|
||||||
<li>View the Project Roadmap (TODO)</li>
|
<li><a href="mailto:me@mediocregopher.com">Report a Bug</a></li>
|
||||||
<li>Report a Bug (TODO)</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>About</h2>
|
<h2>About</h2>
|
||||||
@ -45,3 +40,22 @@ it before:</p>
|
|||||||
individuals for their community of friends and family. By making it super easy
|
individuals for their community of friends and family. By making it super easy
|
||||||
to set up a domain we can help our non-technical folk own their own slice of
|
to set up a domain we can help our non-technical folk own their own slice of
|
||||||
the internet, the way it was always intended.</p>
|
the internet, the way it was always intended.</p>
|
||||||
|
|
||||||
|
<h2>Roadmap</h2>
|
||||||
|
|
||||||
|
<p>Domiply is very much a work in progress. The following functionality is
|
||||||
|
planned but not yet implemented:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Support for AAAA and CNAME records</li>
|
||||||
|
<li>HTTPS support, with automatic certificate syncing via Let's Encrypt.</li>
|
||||||
|
<li>
|
||||||
|
Support for more backends than just git repositories, including:
|
||||||
|
<ul>
|
||||||
|
<li>IPFS/IPNS Hash</li>
|
||||||
|
<li>Alternative URL (reverse proxy)</li>
|
||||||
|
<li>Google Drive / Dropbox</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Scalable backend which requires only an S3 compatible database</li>
|
||||||
|
</ul>
|
||||||
|
Loading…
Reference in New Issue
Block a user