├── .aspell.en.prepl ├── .aspell.en.pws ├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── Makefile ├── check_build.py ├── doc ├── carrier-numbers │ ├── docs.md │ ├── kazoo-carrier.md │ ├── kazoo-numberdoc.md │ ├── kazoo-numberlifecycle.md │ ├── kazoo-provider.md │ └── routing.md ├── component_overview │ └── bigcouch.md ├── config │ ├── account-limits.md │ ├── account_creation.md │ ├── adding-ispeech-tts.md │ ├── basic-sipp.md │ ├── carrier-modules.md │ ├── css-custom.md │ ├── database-health.md │ ├── dns_concepts.md │ ├── email-templates.md │ ├── global-resources.md │ ├── kazoo-config.md │ ├── kazoo-mediastream.md │ ├── limits │ │ ├── commands.md │ │ ├── concepts.md │ │ └── config.md │ ├── lineman-expect.md │ ├── lineman-os.md │ ├── lineman-test.md │ ├── milliwatt-config.md │ ├── monitor-maintain.md │ ├── registrar-design.md │ ├── ssl │ │ ├── .org │ │ │ └── easy_setup.org │ │ ├── easy_setup.md │ │ ├── for_api.md │ │ ├── for_tls.md │ │ └── for_wss.md │ ├── sup-command.md │ └── system_config.md ├── crossbar │ ├── api-design.md │ ├── authentication.md │ ├── haproxy.md │ └── rest-ssl.md ├── extending │ └── authz.md ├── install │ ├── .org │ │ ├── configure_kazoo.org │ │ └── install_via_centos7.org │ ├── configure_kazoo.md │ ├── downloading.md │ ├── downloading_packages_securely.md │ ├── install_manually_centos.md │ ├── install_manually_debian.md │ ├── install_overview.md │ ├── install_via_centos7.md │ ├── install_via_cluster_manager.md │ ├── install_via_debian.md │ ├── install_via_iso.md │ ├── install_via_rpm.md │ ├── minimum_requirements.md │ └── planning_zones.md ├── intro │ ├── about.md │ ├── advanced-concepts.md │ ├── build-own.md │ ├── build_system.md │ ├── cluster-design.md │ ├── component_overview │ │ └── intro.md │ ├── contributing.md │ ├── create-template.md │ ├── disabling-servers.md │ ├── distributed-reg.md │ ├── expand-cluster.md │ ├── g729-licensing.md │ ├── get_help.md │ ├── getting-started.md │ ├── happy-programmer.md │ ├── inbound-call-fail.md │ ├── inbound-call-issue.md │ ├── kazoo-couch.md │ ├── kazoo-media.md │ ├── load-failure.md │ ├── milliwatt.md │ ├── number-lifecycle.md │ ├── read_me_first.md │ ├── resources.md │ ├── startup-sequence.md │ └── storefwd.md ├── kazoo │ ├── call-rating.md │ ├── cluster-guide.md │ ├── dedicated-fs.md │ ├── ecallmgr-cmd.md │ ├── enable-cnam.md │ ├── home-page.md │ ├── kazoo-debug.md │ ├── kazoo-servermisc.md │ ├── kazoo-uimisc.md │ ├── open-source.md │ ├── optimize-kazoo-ui.md │ ├── rating-engine.md │ ├── service-limits.md │ ├── service-plans.md │ ├── sip-flowchart.md │ └── tdm.md ├── mkdocs │ ├── index.md │ └── mkdocs.yml ├── monster │ ├── login-issues.md │ └── optimize-monster.md ├── operation │ ├── cluster-health.md │ ├── ets-persist.md │ ├── login-noapps.md │ ├── monitoring.md │ └── shared_line_appearance.md ├── provisioner │ └── README.md ├── security │ └── iptables.md ├── whapp │ ├── build.md │ ├── jonny5.md │ └── stepswitch.md └── whitelabeling │ └── voicemail_to_email.md ├── fix_docs.bash ├── make └── splchk.mk ├── scripts ├── reconcile_docs_to_index.bash ├── setup_docs.bash └── validate_mkdocs.py └── wiki ├── Confluence-space-export-153629.html.zip ├── Dedicated ├── attachments │ ├── 4194375 │ │ ├── 17694721 │ │ ├── 17694723 │ │ ├── 17694722.png │ │ └── 17694724.png │ ├── 4194509 │ │ ├── 43515905.jpg │ │ ├── 43515906.jpg │ │ ├── 43515907.jpg │ │ ├── 43515908.jpg │ │ ├── 43515909.jpg │ │ ├── 43515910.jpg │ │ ├── 43515911.jpg │ │ ├── 43515912.jpg │ │ ├── 43515913.png │ │ └── 43515914.jpg │ ├── 6488074 │ │ └── 6520834.jpg │ ├── 6979633 │ │ ├── 7208962 │ │ ├── 7208964 │ │ ├── 7208966 │ │ ├── 7208968 │ │ ├── 7634945 │ │ ├── 30015571 │ │ ├── 66453506 │ │ ├── 66453508 │ │ ├── 66453510 │ │ ├── 151650307 │ │ ├── 151650308.png │ │ ├── 30015572.png │ │ ├── 66453507.png │ │ ├── 66453509.png │ │ ├── 66453511.png │ │ ├── 7208963.png │ │ ├── 7208965.png │ │ ├── 7208967.png │ │ ├── 7208969.png │ │ └── 7634946.png │ ├── 7504053 │ │ ├── 7634947 │ │ ├── 7634949 │ │ ├── 7634948.png │ │ └── 7634950.png │ ├── 7504155 │ │ ├── 7634951.png │ │ ├── 7634952.png │ │ ├── 7634953.png │ │ ├── 7634954.png │ │ └── 7634955.png │ ├── 13926531 │ │ ├── 14155781 │ │ ├── 14155783 │ │ ├── 14155785 │ │ ├── 14155782.png │ │ ├── 14155784.png │ │ └── 14155786.png │ ├── 17269210 │ │ ├── 17694734 │ │ └── 17694735.png │ └── 22020107 │ │ └── 22216705.png ├── images │ └── icons │ │ ├── bullet_blue.gif │ │ ├── contenttypes │ │ └── home_page_16.png │ │ └── grey_arrow_down.png ├── service-plans │ └── intro.md └── styles │ └── site.css └── parse_wiki.py /.aspell.en.prepl: -------------------------------------------------------------------------------- 1 | personal_repl-1.1 en 0 utf-8 2 | whapps kapps 3 | justs just 4 | unwieldly unwieldy 5 | likelyhood likelihood 6 | wnm knm 7 | thusly thus 8 | -------------------------------------------------------------------------------- /.aspell.en.pws: -------------------------------------------------------------------------------- 1 | personal_ws-1.1 en 73 utf-8 2 | SHA 3 | IPs 4 | hostnames 5 | CNAM 6 | AMQP 7 | dialplan 8 | ATAs 9 | PSTN 10 | JSON 11 | softswitch 12 | URI 13 | UI 14 | PSAP 15 | rebar 16 | genrsa 17 | config 18 | DHE 19 | HAProxy 20 | sharding 21 | FreeBSD 22 | kapps 23 | ECM 24 | DIDs 25 | ACLs 26 | MongoDB 27 | RSA 28 | Erlang 29 | IVR 30 | transcode 31 | knm 32 | INVITEs 33 | AES 34 | balancers 35 | CDRs 36 | Kamailio 37 | balancer 38 | SMS 39 | schemas 40 | APIs 41 | telcos 42 | ZeroMQ 43 | RTP 44 | rsync 45 | FreeSWITCH 46 | CouchDB 47 | openssl 48 | CSR 49 | DNS 50 | CSS 51 | BigCouch 52 | TLS 53 | RabbitMQ 54 | telco 55 | PCMU 56 | SSL 57 | cURL 58 | datastore 59 | req 60 | CentOS 61 | transcoding 62 | IVRs 63 | PSAPs 64 | Codecs 65 | plaintext 66 | repo 67 | FQDN 68 | WAV 69 | latencies 70 | Schreiber 71 | CAkey 72 | codec 73 | natively 74 | ATA 75 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | python: 3 | version: 2.7.13 4 | post: 5 | - pyenv global 2.7.13 6 | 7 | dependencies: 8 | pre: 9 | - pip -q install --upgrade pip 10 | - pip -q install PyYAML mkdocs pyembed-markdown 11 | 12 | test: 13 | pre: 14 | - make docs 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doc/index.md 2 | /doc/mkdocs/mkdocs.local.yml 3 | /site 4 | /doc/mkdocs/docs/ 5 | /doc/mkdocs/site/ 6 | /doc/mkdocs/theme/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 2600Hz Kazoo and Monster UI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ROOT = $(shell readlink -f .) 2 | DOCS_ROOT=$(ROOT)/doc/mkdocs 3 | 4 | include make/splchk.mk 5 | 6 | docs: docs-report docs-setup docs-build 7 | 8 | docs-report: 9 | @$(ROOT)/scripts/reconcile_docs_to_index.bash 10 | 11 | docs-setup: 12 | @$(ROOT)/scripts/validate_mkdocs.py 13 | @$(ROOT)/scripts/setup_docs.bash 14 | @cp $(DOCS_ROOT)/mkdocs.yml $(DOCS_ROOT)/mkdocs.local.yml 15 | @mkdir -p $(DOCS_ROOT)/theme 16 | @if [ -f $(DOCS_ROOT)/theme/global.yml ]; then cat $(DOCS_ROOT)/theme/global.yml >> $(DOCS_ROOT)/mkdocs.local.yml; fi 17 | 18 | docs-build: 19 | @echo "\ntheme: null\ntheme_dir: '$(DOCS_ROOT)/theme'\ndocs_dir: '$(DOCS_ROOT)/docs'\n" >> $(DOCS_ROOT)/mkdocs.local.yml 20 | @cp $(DOCS_ROOT)/index.md $(ROOT)/doc/index.md 21 | @mkdocs build -f $(DOCS_ROOT)/mkdocs.local.yml --clean -q --site-dir $(DOCS_ROOT)/site 22 | 23 | docs-serve: docs-build 24 | @mkdocs serve --dev-addr=0.0.0.0:9876 -f $(DOCS_ROOT)/mkdocs.local.yml 25 | 26 | docs-clean: 27 | @rm -rf $(DOCS_ROOT)/site $(DOCS_ROOT)/docs $(DOCS_ROOT)/mkdocs.local.yml 28 | -------------------------------------------------------------------------------- /check_build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import yaml, os.path 4 | 5 | def parse_page(page): 6 | "parse a page for existence" 7 | if isinstance(page, dict): 8 | for header, pages in page.items(): 9 | if pages is None: 10 | print "section ", header, " is incomplete" 11 | else: 12 | parse_page(pages) 13 | elif isinstance(page, list): 14 | for p in page: 15 | parse_page(p) 16 | elif isinstance(page, str): 17 | if "index.md" != page and (not os.path.isfile(page)): 18 | print "page ", page, " is not valid" 19 | 20 | stream = open("doc/mkdocs/mkdocs.yml", 'r') 21 | mkdocs = yaml.load_all(stream) 22 | 23 | for doc in mkdocs: 24 | for k,v in doc.items(): 25 | if "pages" == k: 26 | parse_page(v) 27 | -------------------------------------------------------------------------------- /doc/carrier-numbers/docs.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Number Manager 2 | -------------------------------------------------------------------------------- /doc/carrier-numbers/kazoo-carrier.md: -------------------------------------------------------------------------------- 1 | ## Building a Carrier Module 2 | 3 | 4 | 5 | ## Hooking a Carrier's API into Kazoo 6 | 7 | More and more carriers are starting to offer API access to their number search, number provisioning, and other services. Kazoo has a generic method it uses to access these features, allowing the enterprising developer to add a module to Kazoo that does the integration between the carrier and Kazoo. This page will serve as a developer's guide to creating that module. 8 | 9 | 10 | ## Overview 11 | 12 | Carrier modules exist as part of the core's whistle_number_manager application. You can view existing modules in src/carriers/ to help guide your development efforts. A carrier module must export the following functions (this is the **Kazoo** interface): 13 | ``` 14 | find_numbers/3 15 | acquire_number/1 16 | disconnect_number/1 17 | find_number/3 18 | ``` 19 | This is what **Crossbar** and other modules within **Kazoo** would use to search for numbers from a given carrier. The function accepts three arguments and should return a JSON object OK 2-tuple or an error 2-tuple. The first argument is the prefix or number to search for; this will depend on what types of searches the carrier's API supports. It could be the NPA or NPA-NXX in the US, or some other search criteria. The second argument is the quantity of numbers requested by **Kazoo**. The third argument is a proplist, usable for passing carrier-specific options to the underlying module. The JSON object returned should have, as top-level keys, the DIDs found, and the value should be a JSON object containing any additional information (like rate center, features on the number, etc). A sample return could be: 20 | ``` 21 | {"+12223334444":{"sms":"enabled"} 22 | ,"+12223334445":{"sms":"disabled", "ratecenter":"OR"} 23 | } 24 | 25 | acquire_number/1 26 | ``` 27 | This function is responsible for actually provisioning a number from the carrier and having the carrier route the DID to the **Kazoo** installation. It takes as input the #number{} record and expects the modified record back. Typically all that is modified is the `module_data` portion, which is generally appended with any order information the carrier may return. If an error occurs when provisioning the number, use the `knm_number:error_*` functions to throw an appropriate exception. Some carriers will allow you to dynamically add the IP(s) to use when routing the DID to the **Kazoo** cluster. These are typically stored in the `system_config` database in a document for the carrier settings (something like `number_manager.carrier_name`). 28 | 29 | Each `acquire_number` function should also have a dry run capability: 30 | 31 | `acquire_number(#number{dry_run='true'}=Number) -> Number;` 32 | 33 | This allow to bypass the work and not actually buy the number but the rest of the work in **Kazoo** is done. This will return you an updated service plan with the charges that would happen if you bought the number (this work will appear in phone_numbers V2 APIs). 34 | 35 | `disconnect_number/1` 36 | 37 | This function is responsible to releasing the DID from the carrier. Similar to `acquire_number/1`, this function will only return the `#number{}` record on success; otherwise use the `knm_number` error functions to throw exceptions when this function should fail. 38 | -------------------------------------------------------------------------------- /doc/carrier-numbers/kazoo-numberdoc.md: -------------------------------------------------------------------------------- 1 | ## Number Document 2 | 3 | 4 | 5 | ## Number Document 6 | 7 | The number document is the only document used to route inbound calls that originate from a source not associated with an account such as a carrier. Any number that matches the `reconcile_regex`, in the configuration, will generate a number document in the associated database when saved. If the database is not present it will be created the when required. The UI uses a numbers document that is stored in the account database to display the numbers which is kept in synchronization with changes by the number manager. If you make manual changes you will need to also update this document. 8 | 9 | 10 | ## Database 11 | 12 | Number documents are stored in a database based on the prefix of the number after E.164 converters have been applied. All number databases start with "numbers/" followed by the first five digits of the number. For example, 4158867900 would be stored in a database called "numbers/+1415" (URL encoded as "numbers%2F%2B415"). 13 | 14 | 15 | ## Base Parameters 16 | 17 | `PROVIDER_PROPERTIES` - These are provider specific properties that are added to enable some feature (via a provider module). See below for sub-parameters: 18 | 19 | `pvt_module_name` - This property sets carrier module that is responsible for the number. For example if this is set to `knm_some_carrier` then the API module for "Some Carrier" would be used to provision the number or disconnect it. 20 | 21 | `pvt_module_data` - This property is opaque metadata needed by the carrier module to maintain the number. This property should never be manipulated or used by anything other than the carrier module. 22 | 23 | `pvt_number_state` - This property tracks the state in accordance with the Number Lifecycle. 24 | 25 | `pvt_authorizing_account` - This property is the account id that was used to create the number, it is set only during creation and never changes. 26 | 27 | `pvt_assigned_to` - This is the most important property of a number. It is the account id that the number should route to and will only be set on a number that is `in_service` or reserved. 28 | 29 | `pvt_previously_assigned_to` - When an account deletes a number maintained by a carrier module other than knm_local it will transition to the `released_state` (as set in the configuration). When this happens the `pvt_assigned_to` will be cleared and this property will be populated with the account id that it was assigned to. 30 | 31 | `pvt_features` - This property is used to track what features (as supported by provider modules) are active on the number. When a provider is triggered by adding the appropriate properties it will automatically add or remove the associated feature to this list. The primary use of this is for the billing/services module. This parameter is maintained by the number manager and should not be changed directly. 32 | 33 | `pvt_reserve_history` - When an account reserves a number it is added to this ordered list of account ids. As accounts delete the number this list will be used to reserve the number in the next account that previously reserved it. For example, a parent account might reserve a number for use on a sub-account, which adds itself to the reserve history. The subaccount may then acquire the number and use it for a period of time, eventually deleting it. When this happens the number will revert to being reserved on the parent account, instead of moving to a released state. It is only when a number is removed from an account with no reserve history that it actually moves to the released state. 34 | 35 | `pvt_ported_in` - This property is used to track numbers that where created as a port in request. 36 | 37 | `pvt_created` - This is the timestamp that the number was created on, in Gregorian seconds. 38 | 39 | `pvt_modified` - This is the timestamp that the number was lasted modified on, in Gregorian seconds. 40 | 41 | `pvt_db_name` - This is the URL encoded database name that the number document is stored in. 42 | 43 | 44 | ## Sub-Parameters 45 | ``` 46 | cnam 47 | port 48 | dash_e911 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /doc/carrier-numbers/kazoo-numberlifecycle.md: -------------------------------------------------------------------------------- 1 | ## Number Lifecycle 2 | 3 | 4 | 5 | ## Number Lifecycle 6 | 7 | This document describes the lifecycle of direct inward dialing (DID) numbers. 8 | 9 | 10 | ## Number States 11 | 12 | 13 | ## Discovery 14 | 15 | This is an internal number state used to temporarily hold information regarding numbers users can acquire from the system owner carriers. 16 | 17 | Populated (usually) by API requests to the carriers. 18 | 19 | Does not participate in number hunts. 20 | 21 | No accounts have authority to transition to/from this state. 22 | 23 | There are no public fields for "discovery" numbers. 24 | 25 | Any account can transition an "available" number to "reserved". 26 | 27 | 28 | ## Port In 29 | 30 | This is a temporary state for a newly added number by an account via the porting API. 31 | 32 | Automatically transitions to `in_service` the first time hunted by the system (first call to it). 33 | 34 | Does not participate in the `off-net` number hunt, meaning it is only used to route inbound and will not keep calls on-net. 35 | 36 | No accounts have authority to transition to/from this state. 37 | 38 | Any account can create a `port_in number` if it does not exist in the system. 39 | 40 | The public fields of a `port_in` number can be managed by the assigned account or an ancestor of the assigned account. 41 | 42 | Only numbers in the `port_in` state allow the porting documents to be managed. 43 | 44 | These numbers can only exist in states `in_service`, `port_out`, and `disconnected`. None of these transitions can be preformed by accounts. 45 | 46 | 47 | ## Available 48 | 49 | This is a number that has been routed to the cluster but has no account assignment. 50 | 51 | Any account can transition these numbers to `reserved` or `in_service` if they wish to obtain control of it. 52 | 53 | Does not participate in number hunts. 54 | 55 | Any account can transition an `available` number to `reserved`. 56 | 57 | Any account can transition an `available` number to `in_service` if it is not of type `knm_local`. 58 | 59 | There are no public fields for `available` numbers, but they can be created during a transition. 60 | 61 | 62 | ## Reserved 63 | 64 | This is a number that an account has acquired for themselves or their children. Some accounts can create `reserved` numbers. 65 | 66 | Does not participate in number hunts. 67 | 68 | Numbers coming from the `discovery` state will be acquired via the carrier modules. 69 | 70 | 71 | An account can transition a `reserved` number to `in_service` if one of the following criteria is met: 72 | 73 | The requesting account is the same as the assigned account. 74 | 75 | The requesting account is an ancestor of the assigned account. 76 | 77 | The requesting account is an descendant of the assigned account. 78 | 79 | The new account assignment is the same or a descendant of the current assignment. 80 | 81 | 82 | An account can reserve a `reserved` number if one of the following criteria is met: 83 | 84 | The requesting account is an ancestor of the assigned account. 85 | 86 | The requesting account is an descendant of the assigned account. 87 | 88 | The new account assignment is a descendant of the current assignment. 89 | 90 | When a number is reserved the new assignment is added to the history of assignments. 91 | 92 | A flagged account can create a `reserved` number of type `knm_local`. These numbers can only be acquired and managed by the creating 93 | account or a descendant. 94 | 95 | E911 assignments will be maintained, if present. 96 | 97 | The public fields of a `reserved` number can be managed by the assigned account or an ancestor of the assigned account. 98 | 99 | 100 | ## In Service 101 | 102 | This is a number that belongs to an account. 103 | 104 | Participates in number hunts. 105 | 106 | An `in_service` number can be transitioned to "reserved" if the requesting account is, or an ancestor of, the assigned account for the number. 107 | 108 | E911 assignments will be maintained, if present. 109 | 110 | The public fields of an `in_service` number can be managed by the assigned account or an ancestor of the assigned account. 111 | 112 | 113 | ## Released 114 | 115 | This is an aging state that numbers added to the system via discovery remain for a period of time till moving back to available to start the cycle again. 116 | 117 | Does not participate in number hunts. 118 | 119 | *If a `reserved` number is released:* 120 | 121 | With an assignment history it remains `reserved` but is re-assigned to the previously assigned account. 122 | 123 | Without an assignment history and is of type `knm_local` it transitions to `disconnected`. 124 | 125 | Any other type completes the transition to "released". 126 | 127 | *If an "in_service" number is released:* 128 | 129 | With an assignment history it becomes `reserved` and is re-assigned to the previously assigned account. 130 | 131 | Without an assignment history and is of type `knm_local` it transitions to `disconnected`. 132 | 133 | Any other type completes the transition to `released`. 134 | 135 | The public fields of an `released` number are removed. 136 | 137 | Released numbers can be transitioned to `available` by the system or system admin accounts only. 138 | 139 | E911 is disconnected, if present. 140 | 141 | 142 | ## Port Out 143 | 144 | This is an administrative step to releasing numbers. It operates like "Port In" except that inbound requests do not move it to "In Service". 145 | 146 | 147 | ## Disconnected 148 | 149 | This is an internal number state, and marks the number for a permanent move from the active datastore to the archive. 150 | -------------------------------------------------------------------------------- /doc/carrier-numbers/kazoo-provider.md: -------------------------------------------------------------------------------- 1 | # Kazoo Provider Module 2 | 3 | 4 | 5 | ## Hooking a Provider's API into Kazoo 6 | 7 | Service providers offer various add-ons to telephony; things like CNAM, E911, and other services can be added to numbers in **Kazoo**. Creating a provider module links **Kazoo** up with the provider's APIs. 8 | 9 | 10 | ## Overview 11 | 12 | Provider modules exist as part of the core's `whistle_number_manager` application. You can view existing modules in src/providers/ to help guide your development efforts. The exported interface varies based on the type of service being provided. 13 | 14 | Services include: 15 | ``` 16 | E911 17 | CNAM 18 | Porting 19 | E911 20 | # An E911 provider module will export the following interface: 21 | save/1 22 | delete/1 23 | ``` 24 | Each function takes the `#number{}` record and must return a `#number{}` record back to the caller on success. If a failure occurs, an exception is thrown using the `knm_number` error functions. 25 | -------------------------------------------------------------------------------- /doc/carrier-numbers/routing.md: -------------------------------------------------------------------------------- 1 | ## Numbers Document 2 | 3 | 4 | 5 | The numbers document is only used by the APIs to list numbers and has no impact on routing. It exists to reduce the overhead of the `phone_number` API so every number database does not need to be scanned to determine which numbers are active on an account. 6 | The number document is the only document used for routing, however if you are making changes to this document you should ensure they reflect the number document or the user may be confused by conflicting information in the UI. 7 | 8 | 9 | ## Database 10 | 11 | Numbers document are stored in account databases with the id "phone_numbers". 12 | 13 | 14 | ## Base Parameters 15 | 16 | `NUMBER_PROPERTIES` - For each number associated with an account there will be a number property. 17 | 18 | See below for sub-parameters: 19 | 20 | `pvt_type` - This is the type of document and will always have the value of `phone_numbers` example: `pvt_type : phone_numbers` 21 | 22 | `pvt_vsn` - This is the version of the document and indicates which format you will find the data in. Currently these documents are at the first version: `pvt_vsn : "1"` 23 | 24 | `pvt_created` - This is the timestamp that the number was created on, in Gregorian seconds: `pvt_created : 63494331491` 25 | 26 | `pvt_modified` - This is the timestamp that the number was lasted modified on, in Gregorian seconds: `pvt_modified : 63494331491` 27 | 28 | `pvt_account_db` - This is the URL encoded database of the account: `pvt_account_db : account%2F43%2F57%2F9fc0a3aa11e29e960800200c9a66` 29 | 30 | `pvt_account_id` - This is the id of the account: `pvt_account_db : 43579fc0a3aa11e29e960800200c9a66` 31 | ``` 32 | { 33 | "_id": "phone_numbers", 34 | "pvt_account_db": "account%2F43%2F57%2F9fc0a3aa11e29e960800200c9a66", 35 | "pvt_account_id": "43579fc0a3aa11e29e960800200c9a66", 36 | "pvt_vsn": "1", 37 | "pvt_type": "phone_numbers", 38 | "pvt_modified": 63529420348, 39 | "pvt_created": 63520338360, 40 | "+14158867900": { 41 | "state": "in_service", 42 | "features": [ 43 | "dash_e911", 44 | "outbound_cnam", 45 | "inbound_cnam" 46 | ], 47 | "on_subaccount": false, 48 | "assigned_to": "43579fc0a3aa11e29e960800200c9a66" 49 | }, 50 | "+14158867998": { 51 | "state": "in_service", 52 | "features": [ 53 | ], 54 | "on_subaccount": false, 55 | "assigned_to": "43579fc0a3aa11e29e960800200c9a66" 56 | } 57 | } 58 | ``` 59 | 60 | ## Sub-Parameters 61 | 62 | `state` - This is the current number state in accordance with the Number Lifecycle. 63 | 64 | `features` - This property is used to display what features (as supported by provider modules) are active on the number. Changing this directly will have no effect on the actual feature. 65 | 66 | `on_subaccount` - This property is used to determine if a number is used by a sub account. For example, if a parent reserves a number then assigns it to a sub account this value will be true. 67 | 68 | `assigned_to` - This property is used to determine which sub account a number might be assigned to. For example, if a parent reserves a number then assigns it to a sub account this value will be the account id of the sub account. 69 | 70 | ``` 71 | "+14158867900": { 72 | "state": "in_service", 73 | "features": [ 74 | "dash_e911", 75 | "outbound_cnam", 76 | "inbound_cnam" 77 | ], 78 | "on_subaccount": false, 79 | "assigned_to": "43579fc0a3aa11e29e960800200c9a66" 80 | } 81 | 82 | ``` 83 | -------------------------------------------------------------------------------- /doc/component_overview/bigcouch.md: -------------------------------------------------------------------------------- 1 | ## Getting to Know BigCouch 2 | 3 | 4 | 5 | ## Manually Editing Database Documents 6 | 7 | These are the steps required to complete a self install using any of the currently available methods ( Chef-Solo, Packages, or Source Installation). You will access most of these database documents by using the Futon web interface for **Bigcouch**. You can reach it by going to http://ip.address:15984/_utils . Insert the IP Address of your server in the url. Currently we need to manually create the master account on a new deployment. Please replace the (ACCOUNTNAME SIP.REALM.COM USERNAME PASSWORD) placeholders with values that fit your environment. 8 | 9 | `/opt/kazoo/utils/sup/sup crossbar_maintenance create_account ACCOUNTNAME SIP.REALM.COM USERNAME PASSWORD` 10 | 11 | Open your browser and go to **BigCouch** Futon web interface. In the `system_config` database, verify that the `autoload_modules` list in the crossbar document is correct: 12 | 13 | http://ip.address:15984/_utils/document.html?system_config/crossbar 14 | ``` 15 | "autoload_modules": [ 16 | "cb_user_auth", 17 | "cb_simple_authz", 18 | "cb_vmboxes", 19 | "cb_schemas", 20 | "cb_connectivity", 21 | "cb_temporal_rules", 22 | "cb_webhooks", 23 | "cb_global_provisioner_templates", 24 | "cb_shared_auth", 25 | "cb_users", 26 | "cb_global_resources", 27 | "cb_phone_numbers", 28 | "cb_templates", 29 | "cb_signup", 30 | "cb_accounts", 31 | "cb_groups", 32 | "cb_limits", 33 | "cb_local_provisioner_templates", 34 | "cb_callflows", 35 | "cb_local_resources", 36 | "cb_faxes", 37 | "cb_configs", 38 | "cb_api_auth", 39 | "cb_braintree", 40 | "cb_whitelabel", 41 | "cb_onboard", 42 | "cb_conferences", 43 | "cb_directories", 44 | "cb_registrations", 45 | "cb_token_auth", 46 | "cb_clicktocall", 47 | "cb_media", 48 | "cb_menus", 49 | "cb_cdrs", 50 | "cb_servers", 51 | "cb_devices", 52 | "cb_queues", 53 | "cb_rates" 54 | ] 55 | ``` 56 | 57 | Add the FreeSWITCH nodes to the `ecallmgr` document in the `system_config` database. This will instruct `ecallmgr` on which servers to connect to on startup and also what commands to run. In this example, `ecallmgr` will connect to `server1.domain.com` and `server2.domain.com`, run the command `load mod_sofia` and reload the ACLs using the "acls" field. Note that your proxy servers (OpenSIPs or Kamailio) should be in the "authoritative" list while your DID providers should be in the "trusted" list. 58 | 59 | http://ip.address:15984/_utils/document.html?system_config/ecallmgr 60 | ``` 61 | { 62 | "_id": "ecallmgr", 63 | "default": { 64 | "fs_nodes": [ 65 | "freeswitch@server1.domain.com", 66 | "freeswitch@server2.domain.com" 67 | ], 68 | "syslog_log_level": "info", 69 | "fs_cmds": [ 70 | { 71 | "load": "mod_sofia", 72 | "reloadacl": "" 73 | } 74 | ], 75 | "acls": { 76 | "Your Proxy server": { 77 | "type": "allow", 78 | "network-list-name": "authoritative", 79 | "cidr": "xxx.xxx.xxx.xxx/32" 80 | }, 81 | "Your DID provider": { 82 | "type": "allow", 83 | "network-list-name": "trusted", 84 | "cidr": "xx.xx.xx.x/24" 85 | } 86 | } 87 | } 88 | } 89 | ``` 90 | After performing any edits the database documents we will need to flush the cache and restart crossbar. 91 | ``` 92 | /opt/kazoo/utils/sup/sup whapps_config flush 93 | /opt/kazoo/utils/sup/sup -n ecallmgr ecallmgr_config flush 94 | /opt/kazoo/utils/sup/sup whapps_controller restart_app crossbar 95 | ``` 96 | You can now point your browser to http://ip.address to start using the **Kazoo** web interface. Use the credentials from Step 1 to log in, then hover on the white arrow next to your user and account info on the top right of the page and click on "App Store" to turn on the apps you want to use. Have fun and thanks for using **Kazoo**! 97 | -------------------------------------------------------------------------------- /doc/config/account-limits.md: -------------------------------------------------------------------------------- 1 | ## ACCOUNT LIMITS 2 | 3 | 4 | 5 | `twoway_trunks` : 0 //two way flat rate trunks, can only be used if `system_config.jonny5. flat_rate_whitelist` regex matches AND `system_config.jonny5.flat_rate_blacklist` does not. This can also be specified as a `pvt_twoway_trunks`, the lower will be used. 6 | 7 | `inbound_trunks` : 0 //exactly the same as two-way trunks but only used for inbound calls. If there are more inbound calls then trunks two-way trunks will be checked next. 8 | 9 | `resource_consuming_calls` : -1 // hard limit on the number of calls that can consume one or more resources (IE: inbound to outbound forwarded number is one resource consuming call), can also be specified as `pvt_resource_consuming_calls`, the smallest of the two is used 10 | 11 | `calls` : -1 // total number of calls (resource consuming OR NOT) that an account can have, can also be specified as `pvt_calls`, the smallest of the two is used 12 | 13 | `allow_prepay` : true, // allows the customer to disable `per_minute` authorizations relying solely on trunks and/or allotments, this will be overridden by `pvt_allow` prepay 14 | 15 | `pvt_type` : limits 16 | 17 | `pvt_vsn` : 18 | 19 | `pvt_account_id` : d75312345678901234567890 20 | 21 | 22 | `pvt_account_db` : account%2Fd7%2F53%12345678901234567890 23 | 24 | `did_us` : // then classification of a number as per `system_config.number_manager.classifiers` 25 | 26 | `amount` : 120 // the allotment in seconds 27 | 28 | `pvt_created` : 63518278682 29 | 30 | `pvt_modified` : 63518278682 31 | 32 | `pvt_soft_limit_outbound` : false // allows otherwise unauthorized outbound calls to continue 33 | 34 | `pvt_soft_limit_inbound` : true //allows otherwise unauthorized inbound calls to conitnue 35 | 36 | `pvt_allow_prepay` : true 37 | 38 | `pvt_allow_postpay` : true // whether or not to allow the account to go negative 39 | 40 | `pvt_max_postpay_amount` : 100 // the maximum negative amount that is acceptable 41 | 42 | `pvt_allotments`: 43 | 44 | `cycle` : hourly // the allotment reset period: yearly, monthly, weekly, daily, hourly, minutely 45 | 46 | `pvt_discounts`: 47 | 48 | `did_us` : // then classification of a number as per `system_config.number_manager.classifiers` 49 | 50 | `percentage` : 10 // a percentage discount off the rate of EVERY per_minute call 51 | 52 | `pvt_enabeld` : true // should limits be enforced for this account, IF FALSE THE ACCOUNT IS UNRESTRICTED 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /doc/config/adding-ispeech-tts.md: -------------------------------------------------------------------------------- 1 | ## Adding iSpeech TTS to Kazoo 2 | 3 | 4 | 5 | ## iSpeech 6 | 7 | Adding **iSpeech** TTS to **Kazoo** provides a nice TTS engine for turning your dynamic text into a sound file for playing to your callers. Once **Kazoo** is configured properly, **iSpeech** will be used to generate the TTS wav files instead of the `default mod_flite` **FreeSWITCH** module. Generally speaking, the **iSpeech** versions are of a higher quality. As **iSpeech** is a third-party service, you will need to setup and maintain your **iSpeech** account/credits outside of **Kazoo** itself. 8 |   9 | 10 | ## Configure Kazoo to use iSpeech 11 | 12 | 1. Access your **BigCouch**/**CouchDB Futon GUI** (usually located at http://your.host.com:5984/_utils) 13 | 14 | 2. Open up the `system_config` database 15 | 16 | 3. Edit the speech document: 17 | ``` 18 | { 19 | "_id" : "speech", 20 | "default": { 21 | "tts_url": "http://api.ispeech.org/api/json", 22 | "tts_api_key": "YOUR_ISPEECH_KEY", 23 | "tts_frequency": "16000", 24 | "tts_bitrate": "16", 25 | "tts_speed": 0, 26 | "tts_start_padding": 1, 27 | "tts_end_padding": 0, 28 | "tts_provider": "ispeech", 29 | "tts_voice": "female", 30 | "tts_language": "en-US", 31 | "tts_default_voice": "female/en-US", 32 | "tts_url_ispeech": "http://api.ispeech.org/api/json" 33 | } 34 | } 35 | ``` 36 | 37 | These configs should take effect the next time something (such as directory or **Pivot's** Say) utilizes the TTS commands in **Kazoo**. **Kazoo** will proxy/cache the generated .wav file for a time, and normal **FreeSWITCH** media caching will also take place, so an oft-used TTS string should not require accessing **iSpeech** (resulting in audio delays). 38 | -------------------------------------------------------------------------------- /doc/config/basic-sipp.md: -------------------------------------------------------------------------------- 1 | ## Basic SIPp Overview 2 | 3 | 4 | 5 | From http://sipp.sourceforge.net/: **SIPp** is a free Open Source test tool / traffic generator for the SIP protocol. It includes a few basic **SipStone** user agent scenarios (UAC and UAS) and establishes and releases multiple calls with the `INVITE` and `BYE` methods. It can also reads custom XML scenario files describing from very simple to complex call flows. It features the dynamic display of statistics about running tests (call rate, round trip delay, and message statistics), periodic CSV statistics dumps, TCP and UDP over multiple sockets or multiplexed with retransmission management and dynamically adjustable call rates. The **2600hz** team has created a repo with some very simple **SIPp** configurations for use as examples. 6 | 7 | 8 | ## Quick Setup 9 | 10 | Install **SIPp** using your package manager: `yum install -y sipp` 11 |   12 | Clone the **2600hz SIPp** example repo onto a server, such as a **Kazoo** app server: `$ git clone git://github.com/2600hz/sipp.git` 13 | 14 | Create a **Kazoo** account with the Realm : sipp.2600hz.com 15 | 16 | On the website hover over 'Voip Services' then click 'Accounts', click 'New Account' and name the account anything you wish. 17 | 18 | Enter into the Realm **sipp.2600hz.com** and save. 19 | 20 | Click 'Use Account' 21 | 22 | Create a **Kazoo** device: 23 | ``` 24 | username: user_101 25 | password: pwd_101 26 | ``` 27 | 28 | Hover over 'Voip Services' then click 'Devices', 29 | 30 | Click 'Add Device' then click 'SIP Device' and give the device any name you wish. 31 | 32 | Click 'Advanced Settings'then go to the SIP Setting and change: 33 | 34 | ``` 35 | username: user_101 36 | password: pwd_101 37 | ``` 38 | 39 | Click 'Save' 40 | 41 | Create a **Kazoo** device: 42 | ``` 43 | username: user_102 44 | password: pwd_102 45 | ``` 46 | 47 | Click 'Add Device' 48 | 49 | Click 'SIP Device' and give the device any name you wish. 50 | 51 | Click 'Advanced Settings' then go to the 'SIP Setting' 52 | 53 | From there change: 54 | 55 | ``` 56 | username: user_102 57 | password: pwd_102 58 | ``` 59 | 60 | Click 'Save'. 61 | 62 | Create a **Kazoo** device with: 63 | 64 | ``` 65 | username: user_103 66 | password: pwd_103 67 | ``` 68 | 69 | Click 'Add Device' 70 | 71 | Click 'SIP Device' and give the device any name you wish. 72 | 73 | Click 'Advanced Settings' then go to 'SIP Setting' 74 | 75 | Change: 76 | 77 | ``` 78 | username: user_103 79 | password: pwd_103 80 | ``` 81 | 82 | Click 'Save'. 83 | 84 | Enable the 'Check Voicemail' feature code on the account. Hover over 'Voip Services' then click 'Feature Codes'and expand the 'Miscellaneous'section. 85 | 86 | Click the 'Enabled'box by the 'Check Voicemail' feature code and click 'Save'. The **SIPp** test account does not need phone numbers, callflows, or voicemials unless you plan to test them. 87 | 88 | 89 | ## Calls to Voicemail with Authentication 90 | 91 | ``` 92 | call_with_auth.sh 93 | cd dirname $0 94 | sipp -inf users.csv -sf uac_auth.xml -r 1 -d 1000 -s *97 -i 192.168.5.42 192.168.5.151` 95 | -r 1 96 | ``` 97 | 98 | This is the number of calls to start every second, it can be changed while the test it running 99 | 100 | `-d 1000` 101 | 102 | This is how long each call should remain up, in milliseconds 103 | 104 | `-s *97` 105 | 106 | This is is the number to call, in this case the default check voicemail feature code. 107 | 108 | `-i 192.168.5.42` 109 | 110 | This is the IP address to bind to, you will need to set this to the IP of the server running **SIPp**. 111 | 112 | `192.168.5.151 ` 113 | 114 | This is the IP address of the **FreeSWITCH** server to test. Change the two IP address to be the address of the test server and the address of a **FreeSWITCH** server (in that order). When you run this test it will round robin between the SIP credentials in `users.csv` running the test `uac_auth.xml`. 115 | 116 | To start the test type: 117 | 118 | `$ ./call_with_auth.sh` 119 |   120 | 121 | ## Register 122 | 123 | Edit: 124 | ``` 125 | register.sh 126 | cd dirname $0 127 | sipp -inf users.csv -sf register.xml -d 1000 -s 5000 -i 192.168.5.42 192.168.5.151` 128 | -d 1000  129 | ``` 130 | This is ignored by this test 131 | 132 | `-s 5000 ` 133 | 134 | This is ignored by this test 135 | 136 | `-i 192.168.5.42` 137 | 138 | This is the IP address to bind to, you will need to set this to the IP of the server running **SIPp** 139 | 140 | `192.168.5.151 ` 141 | 142 | This is the IP address of the **FreeSWITCH** server to test. 143 | 144 | Change the two IP address to be the address of the test server and the address of a **FreeSWITCH** server (in that order). When you run this test it will round robin between the SIP credentials in `users.csv` running the test `register.xml.` 145 | 146 | To start the test type: 147 | 148 | `$ ./register.sh` 149 | 150 | 151 | ## Leave Voicemails 152 | 153 | Create a 'Voicemail Box' under the **SIPp** test account and create a callflow that goes to that 'Voicemail Box' with the number 5000: 154 | 155 | #Edit 156 | ``` 157 | leave_vm.sh 158 | cd dirname $0 159 | sipp -inf users.csv -sf uac_auth_audio.xml -r 1 -d 5000 -s 5000 -i 192.168.5.42 192.168.5.151 160 | -r 1 161 | ``` 162 | 163 | This is the number of calls to start every second, it can be changed while the test it running. 164 | 165 | `-d 1000` 166 | 167 | This is how long each call should remain up, in milliseconds. 168 | 169 | `-s 5000` 170 | 171 | This is is the number to call, in this case the default check voicemail feature code. 172 | 173 | `-i 192.168.5.42` 174 | 175 | This is the IP address to bind to, you will need to set this to the IP of the server running **SIPp**. 176 | 177 | `192.168.5.151`  178 | 179 | This is the IP address of the **FreeSWITCH** server to test. 180 | 181 | Change the two IP address to be the address of the test server and the address of a **FreeSWITCH** server (in that order). 182 | When you run this test it will round robin between the SIP credentials in `users.csv` running the test `uac_auth_audio.xml`. 183 | 184 | To start the test type: 185 | 186 | `$ ./leave_vm.sh` 187 | -------------------------------------------------------------------------------- /doc/config/css-custom.md: -------------------------------------------------------------------------------- 1 | Customizing CSS and Colors 2 | 3 | Customizing Default Apps 4 |   5 | -------------------------------------------------------------------------------- /doc/config/dns_concepts.md: -------------------------------------------------------------------------------- 1 | ## DNS Concepts 2 | 3 | 4 | 5 | ## What is DNS and why do you need to know: 6 | 7 | 8 | ## What 9 | 10 | Short for Domain Name System (or Service or Server), an Internet service that translates domain names into IP addresses. Because domain names are alphabetic, they're easier to remember. The Internet however, is really based on IP addresses. Every time you use a domain name, therefore, a DNS service must translate the name into the corresponding IP address. For example, the domain name www.example.com might translate to `198.105.232.4`. The DNS system is, in fact, its own network. If one DNS server doesn't know how to translate a particular domain name, it asks another one, and so on, until the correct IP address is returned. 11 | 12 | 13 | ## Why 14 | 15 | To be able to connect to our platform u would need to remember ip addreses, To see the GUI go to `88.25.25.1`, register your phone at `89.25.36.2` and so on. You remember kazoo.io and DNS translates that to the proper IP. 16 | 17 | 18 | ## Thats all? 19 | 20 | Nope.. DNS is awesome as it can do some tricks that really help. Here are a few, add more if you like: 21 | 22 | CNAME records >> They enable you to use your own domainname, but still use a hosted GUI thats not at your own IP or server (a bit like masquerading) 23 | 24 | SRV records >> one of the magic tools that **Kazoo** utilizes. It enables you to specify multiple ip adresses for a single service that maybe delivered by one or more servers. Lets say u have a VoiP service, It runs on a single server and you use my-awesome-domain.com. You grow, get smart and need to add an extra server to your system to share load. 25 | 26 | You have no desire to say to 50% of your clients that they need to register to an other server such as im-growing.my-awesome-domain.com. 27 | SRV solves that for you by giving you the ability to use my-awesome-domain.com but have two ip addreses beneath that. 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /doc/config/email-templates.md: -------------------------------------------------------------------------------- 1 | Activation Email 2 | Voicemail to Email 3 | Low Balance Email 4 |   5 | -------------------------------------------------------------------------------- /doc/config/global-resources.md: -------------------------------------------------------------------------------- 1 | ## GLOBAL RESOURCES 2 | 3 | 4 | 5 | Creating carriers to route outbound calls has never been easier! A single doc for each carrier inside the offnet/ database will make a carrier available to all customers. (Individual customers can optionally set their own carriers in the APIs or via a GUI) 6 | The base doc is pretty simple at this level. Just create a new doc: 7 |   8 | `{_id: random_doc_id, name:my carrier}` 9 |   10 | ## A Carrier 11 | 12 | 13 | Defining a carrier for the system is relatively simple. The keys required are `enabled`, `flag`, `weight_cost`, `route`, and `gateway`: 14 | 15 | `enabled` // toggles whether the carrier is to be included when deciding how to route an outbound call. 16 | 17 | `flag` // a list of features a carrier supports (like CName). This list is matched against a Client DID's options; if all of the features in a DID's options list exist in the carrier's flags, the carrier is kept in the available routes; otherwise it is removed from contention. 18 | 19 | `weight_cost`, when multiple carriers are available to route a call, `weight_cost` allows you to assign which is preferred (by giving it a lower weight). So if you have a primary carrier, assign it 1 (it will be used first). 20 | 21 | `route` // is a list of regular expressions for matching E.164-formatted (+12223334444) DIDs. A sample regex to match all E.164 numbers: 22 | ^\\+1(\\d{10})$ 23 | You can add regexes for specific area codes, toll-free, E911, and international numbers. The first capture group is what is used to pass in the bridge URI (in the example, the 10-digit number will be passed to the gateways). 24 | 25 | `gateway` // a list of gateways provided by the carrier that will handle the routes matched by the regex(s). 26 | 27 | `callerid_type` // an optional field that can toggle how CallerID is passed to the carrier. Potential values are `rpid`, `pid`, and from (corresponding to Remote Party ID, P-*-Identity headers, and From). 28 | 29 | `formatter` // an optional object of formatting instructions for inbound requests from the carrier. 30 | ``` 31 | {_id:5bbc699c76df9da56363233dcc1214bd, pvt_type:resource, name:Some Carrier, enabled:true, flags:[ ], 32 | weight_cost: 30, 33 | rules: [^\\+1(\\d{10})$], 34 | gateways: [... see below ...], 35 | grace_period: 5, 36 | formatters: {request: [ {regex: ^\\+?1?\\d{6}(\\d{4})$, 37 | prefix: ,suffix: }]} 38 | } 39 | 40 |  ``` 41 | 42 | ## Gateways 43 | 44 | 45 | Each gateway has a simple configuration that offers enough flexibility for most carriers. 46 | The only two required fields are server and enabled, but a host of other parameters are available to tweak the setup: 47 | 48 | `server` // hostname or IP of the gateway 49 | 50 | `enabled` // is this gateway available to route over. 51 | 52 | `username` // if the gateway requires a username 53 | 54 | `password` // if the gateway requires a password 55 | 56 | `prefix` // if the gateway requires a prefix on the capture group from the succeeding regex 57 | 58 | `suffix` // if the gateway requires a suffix on the capture group 59 | 60 | `codecs` // a list of codecs to constrain the carrier to during negotiation 61 | 62 | `progress_timeout` // the number of seconds to wait for the gateway to connect the call before failing to the next gateway 63 | To clarify the prefix/suffix/capture group, the route sent to the switch will be built as follows: 64 | 65 | `DID +12223334444` is being called, and the above `regex` is matched. The capture group becomes `2223334444`. If prefix or suffix are not set, they default to, the empty string. The resulting INVITE will look like `PREFIXcapture_groupSUFFIX@SERVER` where the text in caps correspond to the fields above. 66 | 67 | ``` 68 | gateways: [ 69 |   {server: sip001.server.voip_carrier.com 70 | ,username:myacctid 71 | ,password:12345 72 | ,prefix:1717 73 | ,suffix:codecs: [ G729, PCMU, ... ] 74 | ,progress_timeout: 8 // 8 seconds 75 |    ,enabled: true 76 |   } 77 | ] 78 | ``` 79 | 80 | ## Bringing it together 81 | 82 | 83 | Here's the stitched-together carriers document: 84 |  ``` 85 | {_id: 5bbc699c76df9da56363233dcc1214bd 86 | ,pvt_type:resource 87 | ,name:Some Carrier 88 | ,enabled: true 89 | ,flags: [ 90 | ,weight_cost: 3 91 | ,rules: [^\\+1(\\d{10}) ] 92 | ,gateways: [ 93 | {server:sip.carrier.com 94 | ,realm: sip.carrier.com 95 | ,username:username 96 | ,password: password 97 | ,prefix:+ 98 | ,suffix: 99 | ,codecs: [ ] 100 | ,enabled: true 101 | }] 102 | 103 | ,grace_period: 5 104 | ,formatters: {request: [ {regex: ^\\+?1?\\d{6}(\\d{4})$ 105 | ,prefix: 106 | ,suffix: 107 | }] 108 | 109 | ] 110 | 111 | } 112 | ``` 113 | 114 | A carrier is defined. The first has a route that matches E.164 numbers (so US numbers only). The second carrier will match any number starting with 011 or any number that starts with +2-9 (not +1XXXX..., so no US numbers). 115 |   116 |   117 | -------------------------------------------------------------------------------- /doc/config/kazoo-config.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Config 2 | 3 | 4 | ``` 5 | ; config.ini sample 6 | ; section are between [] = [section] 7 | ; key = value 8 | ; to comment add ";" in front of the line 9 | 10 | 11 | ; EXAMPLE 12 | [amqp] 13 | uri = "amqp://guest:guest@localhost:5672" 14 | 15 | [bigcouch] 16 | compact_automatically = true 17 | cookie = change_me 18 | ip = "localhost" 19 | port = 5984 20 | ; username = "XXX" 21 | ; password = "XXX" 22 | admin_port = 5986 23 | 24 | [log] 25 | syslog = info 26 | console = notice 27 | file = error 28 | ``` 29 | -------------------------------------------------------------------------------- /doc/config/kazoo-mediastream.md: -------------------------------------------------------------------------------- 1 | ## Media Streaming 2 | 3 | 4 | 5 | ## Media Anywhere 6 | 7 | Running a PBX, call center, and other telephony-related applications requires custom media files to be accessible to the softswitch processing the call(s). Music-on-hold (MOH), voicemails, auto-attendant menus, etc, are generally created by either the service provider or customers, and any underlying switch in the **Whistle** platform needs access to those files. Copying all media files to all possible servers is not a viable solution. 8 | 9 | 10 | ## What Do You Do? Here's What We Did: 11 | 12 | We store media binary data in **CouchDB** documents as attachments, with the meta-data of the file the properties of the document itself. When **Whistle** receives a request from a **WhApp** to play custom media, Whistle makes a request to any listening **WhApps** for a URL to stream the media from, and the responding **WhApp** (if any, and usually **MediaMgr**) responds with one of two types of URLs, `shout://` and `http://`. 13 | `shout://` 14 | 15 | A URL prepended by `shout://` indicates MP3 media which **FreeSWITCH** plays via mod_shout. **MediaMgr**, when it sees that the requested media is MP3 data, creates a tiny **SHOUTCast** server to stream the MP3 data and a unique URL that is sent back to **Whistle**. **Whistle** feeds this URL to **FreeSWITCH** which then connects to the stream and plays the audio. 16 | http:// 17 | 18 | A URL prepended by `http://` indicates WAV media. Recently, **FreeSWITCH** has added a new module, `mod_shell_stream`, to stream audio from an arbitrary shell command. Combined with a simple sox command, any http URL becomes streamable to **FreeSWITCH**. **MediaMgr** returns an http URL from which it proxies the WAV media from **CouchDB** to **FreeSWITCH**. 19 | 20 | 21 | ## Types of Streams 22 | 23 | Currently we have two streams, new and extant. When **Whistle** requests media data, it indicates whether this is a one-off request (like playing a voicemail) or if it should be joining an already-running stream (like MOH). For one-off requests, "new" is used; for running streams, "extant" is used. If this is the first time the media is requested on an extant stream, the stream is initialized and streaming begun. 24 | 25 | 26 | ## Location Awareness 27 | Sometimes **Whistle** receives a request to play custom media early enough in the Dialplan that **Whistle** can pre-fetch the appropriate URL stream. When this is the case, **Whistle** can wait on several responses from different **MediaMgr** **WhApps** and figure out which is the most appropriate to stream the media from (all media streams in MediaMgr have timeouts, so unused streams will be reclaimed fairly quickly). When rushed, **Whistle** assumes the first responding **WhApp** corresponds to which **WhApp** can also stream the media most efficiently. 28 | -------------------------------------------------------------------------------- /doc/config/limits/commands.md: -------------------------------------------------------------------------------- 1 | Limits : SUP Commands 2 | 3 | -------------------------------------------------------------------------------- /doc/config/limits/concepts.md: -------------------------------------------------------------------------------- 1 | Limits : Concepts 2 | 3 | How limits work... 4 | Enabling limits: 5 | Ensure the default values are appropriate in system_config/jonny5 6 | And those are.... 7 | Ensure jonny5 is configured to start in system_config/whapps_controller 8 | Ensure jonny5 is running 9 | Ensure you have a "default_to" email address in system_config/notify.system_alert to receive notices 10 | Set "authz_enabled" to true on system_config/ecallmgr 11 | Set "authz_default_action" to either "allow" or "deny" on system_config/ecallmgr. This is used when jonny5 fails to reply. 12 | If you just want to test (and not actually limit) set "authz_dry_run" to true on system_config/ecallmgr. 13 | LikeBe the first to like this 14 | -------------------------------------------------------------------------------- /doc/config/limits/config.md: -------------------------------------------------------------------------------- 1 | Limits : Config 2 | 3 | -------------------------------------------------------------------------------- /doc/config/lineman-expect.md: -------------------------------------------------------------------------------- 1 | ## Lineman Expect 2 | 3 | 4 | 5 | The lineman expect tool is a way of defining an expected response from any of the other tools.  This is done by binding to events and specifying the expected value. 6 | 7 | ## Toolbag 8 | 9 | This tool has no configuration parameters. 10 | 11 | 12 | ## Sequence Elements 13 | 14 | All sequence elements are in a expect element and described by the type attribute, which defaults to line if not present. This section will be broken down by the available types (more will be added).  For the available bindings see the other tool pages. 15 | 16 | `expect[@type='line']` 17 | 18 | Compares the expected content with the binding content line by line.  This comparison is implemented by searching for the existence of each line of the expected content in the entire binding content.  Failure to find any line or receive content in the defined timeout will fail the current sequence. 19 | 20 | Attribute / Description / Type 21 | 22 | The value can be line or not present binding. The binding that should carry the expected content timeout. The time in milliseconds to wait for a binding content, defaults to 1000. 23 | 24 | `clean` if this is set to `false` then the XML value will not be cleaned up. See **Lineman Test Tool** for details. 25 |   26 | Example: 27 | ``` 28 | ?xml version=1.0 29 | encoding=ISO-8859-1? 30 | workorder 31 | sequences 32 | sequence 33 | expect binding= freeswitch.fetch_reply.36889abf-8e1b-498e-9f68-d9ad6af3df48 34 | timeout=2000 35 | ![CDATA[ 36 | section name= directory 37 | domain name=test.2600hz.com 38 | user id=device_1 39 | params 40 | param name=password 41 | value= 123456//params 42 | variables 43 | variable name= 44 | ecallmgr_Authorizing-ID 45 | value= e00816343ee126968a5f18a0aa2442e4 46 | variable name= ecallmgr_Inception 47 | value= on-net 48 | variable name=ecallmgr_Authorizing-Type 49 | value=device 50 | variable name=ecallmgr_Account-ID 51 | value=c0705d7474ea0160c10a351b2544006b 52 | variable name=ecallmgr_Realm 53 | value=test.2600hz.com 54 | variable name=ecallmgr_Username 55 | value=device_1 56 | /variable/user/domain/section 57 | 58 | ]] 59 | 60 | 61 | /expect/sequence/sequences/workorder 62 | `` 63 | -------------------------------------------------------------------------------- /doc/config/lineman-os.md: -------------------------------------------------------------------------------- 1 | ## Lineman OS 2 | 3 | 4 | 5 | This tool executes a operating system command.  It can optionally publish the result on a binding so the except tool can compare the result. 6 | 7 | 8 | ## Toolbag 9 | 10 | This tool has no configuration parameters. 11 | 12 | 13 | ## Sequence Elements 14 | 15 | The only sequence element this tool provides is: 16 | 17 | OS 18 | 19 | Attribute 20 | 21 | Description 22 | 23 | Event 24 | 25 | An option event, if specified will be used as the routing key to publish the returned content of the command. Dont forget that sequences run in parallel, use the variables tool to give the event attribute a unique name or your consumers might not be getting the content from this sequence's execution! 26 |   27 | Example: 28 | ``` 29 | ?xml version= 30 | 1.0 31 | encoding= 32 | ISO-8859-1 33 | ? 34 | workorder 35 | øsequences 36 | sequence 37 | os event= 38 | os_command.registrar.local_summary 39 | ![CDATA[ 40 | /opt/whistle/2600hz-platform/utils/command_bridge/command_bridge registrar_maintenance local_summary test.2600hz.com device_1 41 | ]] 42 | /os 43 | /sequence 44 | /sequences 45 | /workorder 46 | ``` 47 | -------------------------------------------------------------------------------- /doc/config/lineman-test.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | 4 | 5 | **Lineman** is a utility for validation and load testing the entire **Whistle** platform.  Once complete this tool will be able to simulate **FreeSWITCH** nodes as well as **BigCouch** allowing full integration testing with mixed component fixtures.  **Lineman** is a modular architecture with three main components: 6 | 7 | **Workorder** - This are a XML representation of a simulation or load test. 8 | 9 | **Toolbag** - This is a collection of tools (or test components) that are available for use in a workorder. 10 | 11 | **Sequences** - This is a set of directives utilizing lineman tools to preform a test or simulation. 12 | 13 | **Workorder** 14 | 15 | A workorder is an XML file that describes the configuration of the lineman tool, data fixtures, and test procedures.  Each workorder defines these three sections as follows: 16 | ``` 17 | ?xml version= 18 | 1.0 19 | encoding= 20 | ISO-8859-1 21 | ? 22 | 23 | workorder 24 | parameters 25 | .../parameters 26 | 27 | toolbag 28 | .../toolbag 29 | 30 | sequences 31 | .../sequences/workorder 32 | ``` 33 | 34 | *Each section will be detailed below* 35 | 36 | ## Parameters 37 | 38 | Parameters element is used to configure lineman for execution of a workorder, this includes how many simultaneous sequences to run at what rate. The available parameters are:  39 | 40 | 41 | ## Element 42 | 43 | 44 | ## Attributes 45 | 46 | 47 | ## Default 48 | 49 | 50 | ## Description 51 | 52 | 53 | ## Name 54 | 55 | 56 | **Unknown** - This is used for display and logging purposes as a descriptive name of for the workorder: 57 | 58 | `max-running-sequences` 59 | 60 | **100** - The maximum simultaneous running sequences that should be allowed: 61 | 62 | `max-sequence-executions` 63 | 64 | **100** - The maximum number of sequences to execute. The test will stop when this is reached. It is not an absolute value as additional sequences can execute depending on the rate/period parameters: 65 | 66 | `sequence-order` 67 | 68 | **sequential** - If a workorder contains multiple sequences this determines the order that they are executed. Currently the only valid options are: 69 | 70 | `sequential` 71 | 72 | `random` 73 | 74 | `sequence-period` 75 | 76 | 77 | **1000** - A period in milliseconds to execute another set of sequences. Every `sequence-period` starts `sequence-rate` sequences: 78 | 79 | `sequence-rate` 80 | 81 | **1** - The number of sequences to start every `sequence-period`: 82 | 83 | `display-period` 84 | 85 | **2000** - A period in milliseconds to display the current lineman status. The lineman status will always be show at the completion of a workorder. 86 |   87 | **Toolbag** - The toolbag element is were each tool is configure prior to starting execution of the sequences.  This section will be processed when the workorder is first loaded, once.  See the individual tool pages for details about the XML elements of this section. 88 | 89 | **Sequences** - The sequences element should contain one or more sequence elements.  At the end of the `sequence-period` a sequence element will be selected based on the `sequence-order` and execution started, for `sequence-rate` elements each period.  If the `sequence-order` is sequential then each sequence element will be started after the previous, in parallel.  However, if the `sequence-order` is 90 | random then one of the sequence elements will be chosen at random for execution during the start phase.   91 | 92 | **Tools** 93 | 94 | 1. FreeSWITCH 95 | 2. Expect 96 | 3. Variables 97 | 4. OS 98 | 5. Sleep 99 | 100 | ...more soon 101 | 102 | ## XML Value Cleaning 103 | 104 | By default all XML element values will be cleaned. This is a process by which leading whitespace is stripped from all lines as well as the first and last newline of only whitespace. This allows the element value to be tabified and on the lines between the open/close XML tags.  However, if the value is sensitive to this process any element can define set a clean attribute to `false` and this process will be skipped. 105 | 106 | 1. Example Workorders 107 | 2. Load Testing Registrar 108 | 3. Registrar Validation 109 | 110 | ...more soon 111 | -------------------------------------------------------------------------------- /doc/config/milliwatt-config.md: -------------------------------------------------------------------------------- 1 | ## Milliwatt Configuration 2 |   3 | 4 | 5 | Create a document (with `milliwatt` for `id`) in the `system_config` database. 6 | 7 | 8 | ## Base Parameters 9 | ``` 10 | echo 11 | duration 12 | ``` 13 | This sets the duration of the echo action. The value is in milliseconds. 14 |   15 | Sample Code: 16 | ``` 17 | duration: 5000 18 | caller_id 19 | ``` 20 | 21 | This sets the caller ids of the echo action. It means that any call made from theses numbers (or users) will be redirected to Milliwatt echo action. 22 |   23 | Sample Code: 24 | ``` 25 | caller_id: [4155555555, username] 26 | number 27 | ``` 28 | 29 | This sets the number of the echo action. It means that any call made 30 | to these numbers will be redirected to Milliwatt echo action. 31 |   32 | Sample Code: 33 | 34 | `number: [123456789,1011121314]` 35 | 36 | Sample Code 37 | ``` 38 | echo: {duration : 5000 39 | ,caller_id: [ 40 | 4155555555,username 41 | ], 42 | 43 | number: [123456789]} 44 | tone 45 | frequencies 46 | ``` 47 | 48 | This sets the frequencies used for the tone action. 49 |   50 | Sample Code 51 | ``` 52 | frequencies: [2600, 2300] 53 | duration 54 | ``` 55 | 56 | This sets the duration of the tone action. The value is in milliseconds. 57 |   58 | Sample Code 59 | ``` 60 | duration: 5000 61 | caller_id 62 | ``` 63 | This sets the caller ids of the tone action. It means that any call made from theses numbers (or users) will be redirected to Milliwatt tone action. 64 |   65 | Sample Code 66 | ``` 67 | caller_id: [4155555555, username] 68 | number 69 | ``` 70 | 71 | This sets the number of the tone action. It means that any call made to these numbers will be redirected to Milliwatt tine action. 72 |   73 | Sample Code 74 | ``` 75 | number: [ 76 | 123456789,1011121314] 77 | ``` 78 | 79 | Sample Code 80 | ``` 81 | tone: { 82 | frequencies: [2600] 83 | ,duration: 5000 84 | ,caller_id: [4155555555 85 | ,username 86 | ] 87 | ,number: [123456789] 88 | } 89 | ``` 90 | 91 | Sample Code: 92 | ``` 93 | {_id: milliwatt,default: {echo: {duration 94 | : 5000 95 | ,caller_id: [username_echo ], 96 | 97 | number: [5555555551] 98 | } 99 | ,tone: {frequencies:[2600] 100 | ,duration: 2000 101 | ,caller_id: [username_tone] 102 | ,number: [5555555552] 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /doc/config/monitor-maintain.md: -------------------------------------------------------------------------------- 1 | ## Monitor Maintain Whistle_Apps 2 | 3 | 4 | 5 | Connect to the Erlang `whistle_apps` shell: 6 |   7 | `whistle_apps/conn-to-apps.sh` 8 |   9 | Flushing the cache (callflows, CID, ect): 10 |   11 | `wh_cache:flush().` 12 |   13 | Flushing the `config` (db cached system config) requires restart of `sysconf whapp`: 14 | ```  15 | whapps_config:flush(). 16 | whapps_controller:restart_app(sysconf). 17 | ```  18 | 19 | **Erlang**-based Application list (full): 20 |   21 | `application:which_applications().` 22 |   23 | Start, stop **WhApps** and verify running **WhApps**: 24 | ```  25 | whapps_controller:start_app(crossbar). 26 | whapps_controller:stop_app(crossbar). 27 | whapps_controller:running_apps(). 28 | ```  29 | 30 | Determine which **CouchDB/BIGCouch** server it is connected to 31 | ```  32 | couch_mgr:get_host(). 33 | couch_mgr:get_creds(). 34 | ``` 35 | 36 | Forcing Compaction 37 | ```  38 | couch_compactor:start_link(). 39 | couch_compactor:force_compaction(). 40 | ```  41 | 42 | Determine which **RabbiMQ** you are connecting to: 43 | ```  44 | amqp_mgr:get_host(). 45 | amqp_mgr:is_available(). 46 |  ``` 47 | 48 | **Stepswitch** Commands 49 |  ``` 50 | stepswitch_maintenance:reconcile(). 51 | stepswitch_maintenance:reconcile(ACCOUNT_ID). 52 | stepswitch_maintenance:reload_resources(). 53 | stepswitch_maintenance:lookup_number(5552223333). 54 | stepswitch_maintenance:process_number(5552223333). 55 |  ``` 56 | 57 | **Whapps** Maintenance (useful for updating the global views in **BigCouch** on an install) 58 | ``` 59 | whapps_maintenance:refresh(). 60 | whapps_maintenance:refresh(Account ID). 61 | ``` 62 | 63 | NOTE - THIS REPLACED: 64 | 65 | `crossbar:refresh().` 66 | 67 | Callflow Refresh Command (useful for after upgrade) 68 |   69 | `callflow_maintenance:refresh().` 70 |   71 | Whistle Number Manager (iterates thru each account checking all DID's exist in the numbers db) 72 |   73 | `whistle_number_manager_maintenance:reconcile().` 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /doc/config/registrar-design.md: -------------------------------------------------------------------------------- 1 | ## Register Startup 2 | 3 | 4 | 1. On startup of Registrar, prime the cache by looking up the registrations currently stored in the DB Registration Attempt. 5 | 2. Phone sends registration to SBC, which load-balances the request to the next switch in its list. 6 | 3. Switch sends an auth challenge back 7 | 4. Phone sends registration with digest data 8 | 5. Switch forwards request to Whistle 9 | 6. **Whistle** creates an `authn_req`AMQP message 10 | 7. Registrar receives `authn_req` AMQP message 11 | 8. If credentials are found, Registrar sends an `authn_resp` AMQP message 12 | 9. **Whistle** receives the response and sends the switch the appropriately formatted response. 13 | 10. Phone is notified of success or failure 14 | 11. Using the `Auth-User` and `Auth-Realm`, lookup the sip credentials for this combination 15 | 12. Check the in-memory cache for the sip credential object. 16 | 13. If found, return the object else, lookup in the DB for the user/realm combination and cache the response 17 | 14. If the user/realm isn't found, this process stop processing immediately 18 | 15. Create the `authn_resp` AMQP message 19 | 16. Send it directly to the requesting **Whistle** process 20 | 17. The phone is now registered (or not). Behind the scenes, there is more to be done though. **Whistle**, upon learning of a successful registration, will create a `reg_success` AMQP message for which Registrar is also listening. Upon receipt: 21 | 18. Cleanup the contact string 22 | 19. Create the cache ID from the contact string (md5'ed) 23 | 24 | ## Lookup in the cache: 25 | 26 | 1. If the cache ID exists: update the cache with the latest registration JSON object. 27 | 2. If it doesn't exist: remove the old registrations for the username/realm from the database. 28 | 3. Store the registration in the cache and a mapping of username/realm to the cache ID. 29 | 4. Store the registration in the database. 30 | 31 | 32 | ## Querying the Registrar 33 | 34 | 1. Sending a `reg_query` on AMQP is picked up by Registrar 35 | 2. Using the username/realm, lookup the registration cache ID. 36 | 3. If the cache ID exists, lookup the registration JSON object. 37 | 4. If the cache ID doesn't exist, the process exits. 38 | 5. Based on what Fields are requested in the query, a response is created from the cached registration JSON. 39 |   40 | 41 | ## MISC 42 | 43 | The Registration/Authentication **WhApp**, responsible for receiving auth/reg events off AMQP from `ecallmgr`, querying a SIP credentials DB, and returning a response to AMQP on successful lookup. A registration doc will also be created/updated on success (may need to figure out how nonce/cnonce work for verifying the password). 44 | 45 | **Registration Success Message** 46 | 47 | **Event-Category** `directory` required 48 | 49 | **Event-Name** `reg_success` required 50 | 51 | **Event-Timestamp** `timestamp` required 52 | 53 | **From-User** `username` required 54 | 55 | **From-Host** `host.com` required 56 | 57 | **Contact** `contact@host.com:1234;other=params` required 58 | 59 | **RPid** unknown, maybe others required 60 | 61 | **Status** some status optional 62 | 63 | **Expires** time,in seconds required 64 | 65 | **To-User** `username` required 66 | 67 | **To-Host** 1.2.3.4 required 68 | 69 | **Network-IP** 1.2.3.4 required 70 | 71 | **Network-Port** 12345 required 72 | 73 | **Username** `username` required 74 | 75 | **Realm** 1.2.3.4 required 76 | 77 | **User-Agent** Whistle-1.0 optional 78 | 79 | 80 | ## Querying the Registration WhApp 81 | 82 | Given user credentials, you can retrieve the fields listed above in the 'Registration Success' message for any registrations that have not expired. 83 | 84 | **Event-Category** directory required 85 | 86 | **Event-Name** `reg_query` required 87 | 88 | **Username** string required 89 | 90 | **Realm** string required 91 | 92 | **Fields** `[whistle:Field]` required 93 | 94 | 95 | Your application will receive the following message as response: 96 | 97 | **Event-Category** directory required 98 | 99 | **Event-Name** `reg_query_resp` required 100 | 101 | **Fields** `[whistle:{Field, Value}]` required 102 | -------------------------------------------------------------------------------- /doc/config/ssl/.org/easy_setup.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:{} arch:headline 2 | #+OPTIONS: author:t broken-links:nil c:nil creator:nil 3 | #+OPTIONS: d:(not "LOGBOOK") date:t e:t email:nil f:t inline:t num:t 4 | #+OPTIONS: p:nil pri:nil prop:nil stat:t tags:t tasks:t tex:t 5 | #+OPTIONS: timestamp:t title:t toc:t todo:t |:t 6 | #+TITLE: Easy Setup of SSL 7 | #+DATE: <2016-11-21 Mon> 8 | #+AUTHOR: James Aimonetti 9 | #+EMAIL: james@2600hz.com 10 | #+LANGUAGE: en 11 | #+SELECT_TAGS: export 12 | #+EXCLUDE_TAGS: noexport 13 | #+CREATOR: Emacs 26.0.50.1 (Org mode 9.0) 14 | 15 | * Quick SSL Setup Guide 16 | 17 | This guide assumes you have configured your DNS properly to point to the server you wish to secure. It will assume you are using Apache to serve MonsterUI and as a reverse proxy for Crossbar (the API server) for SSL termination. 18 | 19 | ** Let's Encrypt Cert Setup 20 | 21 | We'll use [[https://letsencrypt.org/][Let's Encrypt]] to generate a free SSL certificate for us. See [[https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-14-04][these instructions]] for a more detailed guide. 22 | #+BEGIN_SRC bash 23 | # First, get the script from EFF 24 | sudo wget -O /usr/local/sbin https://dl.eff.org/certbot-auto 25 | 26 | # Make it executable 27 | sudo chmod a+x /usr/local/sbin/certbot-auto 28 | #+END_SRC 29 | 30 | *** Setup Let'sEncrypt and Apache 31 | 32 | To start an interactive session to setup the certificate for your domain (in this case, kazoo.mycompany.com): 33 | 34 | #+BEGIN_SRC bash 35 | certbot-auto --apache -d kazoo.mycompany.com 36 | #+END_SRC 37 | 38 | Certificates will be installed to `/etc/letsencrypt/live` 39 | 40 | *** Auto-renew 41 | 42 | Let's Encrypt certificates are valid for 90 days. Triggering the renewal process is straight-forward: 43 | 44 | #+BEGIN_SRC bash 45 | certbot-auto renew 46 | #+END_SRC 47 | 48 | Setup auto-renewal in the form of a cronjob: 49 | 50 | #+BEGIN_SRC bash 51 | sudo crontab -e 52 | #+END_SRC 53 | 54 | #+BEGIN_SRC crontab 55 | 30 2 * * 1 /usr/local/sbin/certbot-auto renew >> /var/log/le-renew.log 56 | #+END_SRC 57 | ** Setup Apache as a reverse proxy 58 | 59 | Having Apache (or any HTTP server) proxy the requests for the API server makes sense. You can manage your certificates in fewer places and API servers can come and go since each request is independent of any others (no state shared between requests on a given API server). 60 | 61 | We create two VirtualHost entries, one for serving MonsterUI assets and one for proxying to Crossbar. 62 | *** MonsterUI 63 | #+BEGIN_SRC apache 64 | 65 | ServerName kazoo.mycompany.com:443 66 | 67 | DocumentRoot /var/www/html/monster-ui 68 | 69 | SSLEngine on 70 | SSLCertificateKeyFile "/etc/letsencrypt/live/kazoo.mycompany.com/privkey.pem" 71 | SSLCertificateFile "/etc/letsencrypt/live/kazoo.mycompany.com/cert.pem" 72 | 73 | 74 | #Options FollowSymLinks 75 | Options Indexes FollowSymLinks Includes ExecCGI 76 | AllowOverride All 77 | Order deny,allow 78 | Allow from all 79 | 80 | 81 | #+END_SRC 82 | *** API Reverse Proxy 83 | Be sure to replace the IPs with the IP Crossbar is using. 84 | #+BEGIN_SRC apache 85 | 86 | ServerName kazoo.mycompany.com:8443 87 | ProxyPreserveHost On 88 | 89 | SSLEngine on 90 | SSLCertificateKeyFile "/etc/letsencrypt/live/pdx.2600hz.com/privkey.pem" 91 | SSLCertificateFile "/etc/letsencrypt/live/pdx.2600hz.com/fullchain.pem" 92 | 93 | ProxyPass / http://10.1.10.29:8000/ 94 | ProxyPassReverse / http://10.1.10.29:8000/ 95 | 96 | #+END_SRC 97 | 98 | *** Reconfigure MonsterUI and the Apps 99 | 100 | Once you've reloaded Apache, you'll want to update MonsterUI's config.js: 101 | 102 | #+BEGIN_SRC bash 103 | vim /var/www/html/monster-ui/js/config.js 104 | # Update api_url to 'https://kazoo.mycompany.com:8443/v2' 105 | #+END_SRC 106 | 107 | And re-init the apps: 108 | #+BEGIN_SRC bash 109 | # Re-initialize Monster Apps 110 | sup crossbar_maintenance init_apps \ 111 | /var/www/html/monster-ui/apps \ 112 | https://kazoo.mycompany.com:8443/v2 113 | #+END_SRC 114 | -------------------------------------------------------------------------------- /doc/config/ssl/easy_setup.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Quick SSL Setup Guide 4 | 5 | This guide assumes you have configured your DNS properly to point to the server you wish to secure. It will assume you are using Apache to serve MonsterUI and as a reverse proxy for Crossbar (the API server) for SSL termination. 6 | 7 | 8 | 9 | 10 | ## Let's Encrypt Cert Setup 11 | 12 | We'll use [Let's Encrypt](https://letsencrypt.org/) to generate a free SSL certificate for us. See [these instructions](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-14-04) for a more detailed guide. 13 | 14 | ```bash 15 | # First, get the script from EFF 16 | sudo wget -O /usr/local/sbin https://dl.eff.org/certbot-auto 17 | 18 | # Make it executable 19 | sudo chmod a+x /usr/local/sbin/certbot-auto 20 | ``` 21 | 22 | 23 | 24 | 25 | ### Setup Let'sEncrypt and Apache 26 | 27 | To start an interactive session to setup the certificate for your domain (in this case, kazoo.mycompany.com): 28 | 29 | ```bash 30 | certbot-auto --apache -d kazoo.mycompany.com 31 | ``` 32 | 33 | Certificates will be installed to \`/etc/letsencrypt/live\` 34 | 35 | 36 | 37 | 38 | ### Auto-renew 39 | 40 | Let's Encrypt certificates are valid for 90 days. Triggering the renewal process is straight-forward: 41 | 42 | ```bash 43 | certbot-auto renew 44 | ``` 45 | 46 | Setup auto-renewal in the form of a cronjob: 47 | 48 | ```bash 49 | sudo crontab -e 50 | ``` 51 | 52 | ```crontab 53 | 30 2 * * 1 /usr/local/sbin/certbot-auto renew >> /var/log/le-renew.log 54 | ``` 55 | 56 | 57 | 58 | 59 | ## Setup Apache as a reverse proxy 60 | 61 | Having Apache (or any HTTP server) proxy the requests for the API server makes sense. You can manage your certificates in fewer places and API servers can come and go since each request is independent of any others (no state shared between requests on a given API server). 62 | 63 | We create two VirtualHost entries, one for serving MonsterUI assets and one for proxying to Crossbar. 64 | 65 | 66 | 67 | 68 | ### MonsterUI 69 | 70 | ```apache 71 | 72 | ServerName kazoo.mycompany.com:443 73 | 74 | DocumentRoot /var/www/html/monster-ui 75 | 76 | SSLEngine on 77 | SSLCertificateKeyFile "/etc/letsencrypt/live/kazoo.mycompany.com/privkey.pem" 78 | SSLCertificateFile "/etc/letsencrypt/live/kazoo.mycompany.com/cert.pem" 79 | 80 | 81 | #Options FollowSymLinks 82 | Options Indexes FollowSymLinks Includes ExecCGI 83 | AllowOverride All 84 | Order deny,allow 85 | Allow from all 86 | 87 | 88 | ``` 89 | 90 | 91 | 92 | 93 | ### API Reverse Proxy 94 | 95 | Be sure to replace the IPs with the IP Crossbar is using. 96 | 97 | ```apache 98 | 99 | ServerName kazoo.mycompany.com:8443 100 | ProxyPreserveHost On 101 | 102 | SSLEngine on 103 | SSLCertificateKeyFile "/etc/letsencrypt/live/pdx.2600hz.com/privkey.pem" 104 | SSLCertificateFile "/etc/letsencrypt/live/pdx.2600hz.com/fullchain.pem" 105 | 106 | ProxyPass / http://10.1.10.29:8000/ 107 | ProxyPassReverse / http://10.1.10.29:8000/ 108 | 109 | ``` 110 | 111 | 112 | 113 | 114 | ### Reconfigure MonsterUI and the Apps 115 | 116 | Once you've reloaded Apache, you'll want to update MonsterUI's config.js: 117 | 118 | ```bash 119 | vim /var/www/html/monster-ui/js/config.js 120 | # Update api_url to 'https://kazoo.mycompany.com:8443/v2' 121 | ``` 122 | 123 | And re-init the apps: 124 | 125 | ```bash 126 | # Re-initialize Monster Apps 127 | sup crossbar_maintenance init_apps \ 128 | /var/www/html/monster-ui/apps \ 129 | https://kazoo.mycompany.com:8443/v2 130 | ``` 131 | -------------------------------------------------------------------------------- /doc/config/ssl/for_api.md: -------------------------------------------------------------------------------- 1 | Configuring SSL for Crossbar 2 | -------------------------------------------------------------------------------- /doc/config/ssl/for_tls.md: -------------------------------------------------------------------------------- 1 | SSL for TLS 2 | 3 | -------------------------------------------------------------------------------- /doc/config/ssl/for_wss.md: -------------------------------------------------------------------------------- 1 | SSL for WSS 2 | 3 | -------------------------------------------------------------------------------- /doc/config/system_config.md: -------------------------------------------------------------------------------- 1 | System Configuration Documents 2 | -------------------------------------------------------------------------------- /doc/crossbar/api-design.md: -------------------------------------------------------------------------------- 1 | ## Crossbar API Design 2 | 3 | 4 | 5 | ## API Design 6 | 7 | Everyone thinks the APIs are so mysterious so here's a little secret. The APIs are actually a very simple pass-thru mechanism with some logic in the middle: 8 | 9 | Request From Browser --> HTTP Server --> Authentication Check --> Data Validation --> (Some Routing Magic) --> Save to Database (without changing the format of the original request) 10 | 11 | In other words, in almost every API request: 12 | 13 | 1. We read a document from **CouchDB**, in **JSON** format 14 | 2. We strip any fields with `pvt_ and _ at` the beginning of the field names out 15 | 3. We send it to you via the API 16 | 17 | So what's in the DB is what you get back via API calls. And what you send to the API works in reverse but the same - if you send it, we store it. That means if you want to introduce new features, custom fields, etc. well, you can! Just add them to your API requests and change the validation to allow those new fields. The validation criteria is also kept in the database (makes it easy to change) - check out the `system_schemas` database. Add or remove at will. 18 | 19 | The only things layered "in-between" your request and the database are: 20 | 21 | Validation, which is done off configurable documents - check out the `system_schemas` database 22 | 23 | Note: The schemas also allow you to auto-add default values if they are missing, when a document is created or updated 24 | 25 | Authentication. You must have access to the account, based on your `auth_token` 26 | 27 | Eventing. You can listen for events when someone stores or retrieves something 28 | 29 | Some internationalization and upload/binary file handling, in some cases 30 | 31 | A message bus to transport the requests to/from the apps 32 | -------------------------------------------------------------------------------- /doc/crossbar/authentication.md: -------------------------------------------------------------------------------- 1 | ## Enabling / Disabling Authentication 2 | 3 | Only turn authentication on and/or authorization off on a non-public server. Servers with no authentication can be easily compromised. 4 | 5 | **Kazoo** comes with two modules that allow you to bypass the authentication and authorization mechanisms, but has them turned off by default. Enabling them is relatively easy. These instructions assume the **Crossbar** application is running in the **kazoo_apps** container and is listening on port 8000, the default. 6 | 7 | ## Steps to Turn Off Auth 8 | ``` 9 | sup crossbar_module_sup start_mod cb_noauthn 10 | sup crossbar_module_sup start_mod cb_noauthz 11 | ``` 12 | 13 | ## What did we just do? 14 | 15 | We just loaded two dummy modules, one for authentication and one for authorization. These modules listen for authentication and authorization requests and blindly reply to them in the affirmative. You must still provide credentials when performing checks on the system, but the credentials (such as a username and password, or an X-Auth-Token) can be bogus. 16 | 17 | ## Turning Auth Back On 18 | ``` 19 | sup crossbar_module_sup stop_mod cb_noauthn 20 | sup crossbar_module_sup stop_mod cb_noauthz 21 | ``` 22 | 23 | ## What did we just do? 24 | 25 | We just stopped and unloaded the two dummy modules that blindly reply to authentication and authorization requests. You will now be enforcing username/password and auth token requirements as normal. 26 | 27 | To verify the enabling/disabling 28 | 29 | XXXX 30 | 31 | -------------------------------------------------------------------------------- /doc/crossbar/haproxy.md: -------------------------------------------------------------------------------- 1 | ## HAProxy 2 | 3 | 4 | 5 | *Use HAProxy 1.5 to create an SSL reverse proxy* 6 | 7 | If you're working from an existing install, you will likely need to remove HAProxy 1.4 before continuing. Be sure to take a backup of `/etc/haproxy/haproxy.cfg`! 8 | 9 | `yum erase haproxy` 10 | 11 | We are going to build **HAProxy** from a source `rpm`, so we need to install a few things: 12 | 13 | `yum install @development openssl-devel pcre-static pcre-devel` 14 | 15 | Then we download a source `rpm` to build from: 16 | 17 | `curl -O http://dagobah.ftphosting.net/yum/SRPMS/haproxy-1.5-dev14.src.rpm` 18 | 19 | Then we build the `rpm`: 20 | 21 | `rpmbuild --rebuild haproxy-1.5-dev14.src.rpm ` 22 | 23 | Then we install the `rpm` we just built: 24 | 25 | `rpm -Uvh rpmbuild/RPMS/x86_64/haproxy-1.5-dev14.x86_64.rpm` 26 | 27 | Let's move the original config back in place: 28 | 29 | `mv /etc/haproxy/haproxy.cfg.rpmsave /etc/haproxy/haproxy.cfg` 30 | 31 | Let's make a directory for the cert/s: 32 | 33 | `mkdir -p /etc/haproxy/certs` 34 | 35 | Put your `pem` cert/key into the `certs` folder: 36 | 37 | `mv certificate.pem /etc/haproxy/certs` 38 | 39 | Add the following to the `/etc/haproxy/haproxy.cfg config`: 40 | 41 | ``` 42 | frontend whapps-ssl-in 43 | bind *:8443 ssl crt /etc/haproxy/certs/certificate.pem 44 | default_backend whapps 45 | backend whapps 46 | balance roundrobin 47 | server localhost host.domain.com:8000 check 48 | ``` 49 | 50 | Restart **HAProxy** and enjoy! 51 | 52 | 53 | ## Cleanup: 54 | 55 | Update the `/var/www/html/config/config.js` for the new `https`: and `port` 56 | 57 | You may need to update the endpoint entries for existing users to point to the new `https`: and `port`, you can see how to do that here: 58 | 59 | 60 | ## Manually Editing Database Documents 61 | 62 | **Notes:** 63 | 64 | If you have an existing cert, `ca-bundle` and `key`, here's how you can make the `pem`: 65 | 66 | `cat certificate.crt ca-bundle.crt private.key > certificate.pem` 67 | 68 | Hopefully it's obvious that the paths, and host names need to be updated for your environment. 69 | -------------------------------------------------------------------------------- /doc/extending/authz.md: -------------------------------------------------------------------------------- 1 | ## Hook Auth Externally 2 | 3 | 4 | 5 | **Kazoo** allows you to hook to authentication and authorization requests externally. This makes it simple to attach a billing or single sign-on system externally. The concept is easy to understand. Each time a username/password needs verification, we'll make a call to your external system to check if it's valid. Each time a phone number is dialed, we'll make a call to your external system to make sure it's OK for the identified customer to make that call. 6 | -------------------------------------------------------------------------------- /doc/install/.org/configure_kazoo.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:{} arch:headline 2 | #+OPTIONS: author:t broken-links:nil c:nil creator:nil 3 | #+OPTIONS: d:(not "LOGBOOK") date:t e:t email:nil f:t inline:t num:t 4 | #+OPTIONS: p:nil pri:nil prop:nil stat:t tags:t tasks:t tex:t 5 | #+OPTIONS: timestamp:t title:t toc:t todo:t |:t 6 | #+TITLE: configure_kazoo 7 | #+DATE: <2016-11-15 Tue> 8 | #+AUTHOR: James Aimonetti 9 | #+EMAIL: james@2600hz.com 10 | #+LANGUAGE: en 11 | #+SELECT_TAGS: export 12 | #+EXCLUDE_TAGS: noexport 13 | #+CREATOR: Emacs 26.0.50.2 (Org mode 9.0) 14 | 15 | * Configuring Kazoo 16 | This guide assumes you've installed Kazoo via one of the supported methods and are now ready to create devices, users, carriers, etc. 17 | ** API Basics 18 | Kazoo requires an auth-token for most API usage. You can create a token via a number of ways but we'll just use the username/password we created in the installation guide. 19 | 20 | #+BEGIN_SRC bash 21 | # User/Pass credentials hash 22 | echo -n "{USERNAME}:{PASSWORD}" | md5sum 23 | {MD5_HASH} - 24 | 25 | # Copy the {MD5_HASH} and create an Auth Token 26 | curl -v -X PUT -H "content-type:application/json" \ 27 | -d '{"data":{"credentials":"{MD5_HASH}","account_name":"{ACCOUNT_NAME}"}}' \ 28 | http://ip.add.re.ss:8000/v2/user_auth | python -mjson.tool 29 | 30 | # Export the "auth_token" and "account_id" for easy use in later API requests 31 | export AUTH_TOKEN="{AUTH_TOKEN}" 32 | export ACCOUNT_ID="{ACCOUNT_ID}" 33 | #+END_SRC 34 | 35 | Now your shell will have an auth token and account id to use (please export the real values and not the {...} placeholders. 36 | ** Create a device 37 | *** Via API 38 | #+BEGIN_SRC bash 39 | # Create a base device 40 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 41 | -d '{"data":{"name":"Device1"}}' \ 42 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/devices | python -mjson.tool 43 | 44 | # capture the "id" of the device 45 | export DEVICE_ID="{DEVICE_ID}" 46 | 47 | # Add a terrible username and password 48 | curl -X PATCH -H "X-Auth-Token: $AUTH_TOKEN" \ 49 | -d '{"data":{"sip":{"username":"device_1","password":"password_1"}}}' \ 50 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/devices/$DEVICE_ID | python -mjson.tool 51 | #+END_SRC 52 | 53 | Using the realm of the account, you should now be able to register a phone using the credentials created. 54 | *** Via MonsterUI 55 | Use SmartPBX - Screenshots welcomed 56 | ** Create a callflow for the device 57 | *** Via API 58 | #+BEGIN_SRC bash 59 | # create a callflow for extension 1001 to ring the device 60 | # note: we need to escape the quotes to use $DEVICE_ID in the JSON data 61 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 62 | -d "{\"data\":{\"name\":\"Device1 Callflow\", \"numbers\":[\"1001\"], \"flow\":{\"module\":\"device\",\"data\":{\"id\":\"$DEVICE_ID\"}}}}" \ 63 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 64 | #+END_SRC 65 | 66 | You should now be able to create a second device and call 1001 to ring the first device 67 | ** Create an outbound carrier 68 | 69 | This assumes you have an upstream carrier that uses username/password to authenticate your calls. 70 | #+BEGIN_SRC bash 71 | # Create a "resource" representing the carrier 72 | # "rules" is a list of regexes to match numbers for this carrier 73 | # "gateways" is a list of JSON objects representing the gateway(s) to use 74 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 75 | -d '{"data":{"rules":[".{5,}"],"name":"Carrier Foo","gateways":[{"realm":"sip.carrier.com","server":"sip.carrier.com","username":"your_username","password":"your_password","enabled":true}]}}' \ 76 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/resources | python -mjson.tool 77 | 78 | # capture the id of the resource 79 | export RESOURCE_ID="{RESOURCE_ID}" 80 | 81 | # Now create a callflow to use this account resource 82 | # This uses the "no_match" special number 83 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 84 | -d '{"data":{"name":"Offnet Callflow","numbers":["no_match"],"flow":{"module":"resources","data":{"use_local_resources":true}}}}' \ 85 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 86 | #+END_SRC 87 | 88 | If you use the regex above, any number 5 digits or more will route to your carrier. 89 | ** Route numbers to your setup 90 | 91 | Getting numbers to route in Kazoo requires a few steps. This guide will use the defaults in the system (read: mostly US-based numbers) to make this fast. Alternative documentation should be created for handling other areas of the world. 92 | 93 | 1. Add the carrier to the ACLs 94 | #+BEGIN_SRC bash 95 | sup ecallmgr_maintenance allow_carrier CarrierFoo 1.2.3.4/32 96 | #+END_SRC 97 | You can set the IP as a raw IPv4 IP address or in [[https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation][CIDR notation]]. 98 | 2. Add a number that you expect your carrier to route to you 99 | #+BEGIN_SRC bash 100 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 101 | -d '{"data":{}}' \ 102 | "http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/phone_numbers/+15551234567" | python -mjson.tool 103 | 104 | # Activate the number 105 | curl -v -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 106 | -d '{"data":{}}' \ 107 | "http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/phone_numbers/+15551234567/activate" | python -mjson.tool 108 | #+END_SRC 109 | 3. Create a callflow for that DID. You could also amend the callflow created for the first device, adding the number to its "numbers" array. 110 | #+BEGIN_SRC bash 111 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 112 | -d "{\"data\":{\"name\":\"Main Callflow\",\"numbers\":[\"+15551234567\"],\"flow\":{\"module\":\"device\",\"data\":{\"id\":\"$DEVICE_ID\"}}}}" \ 113 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 114 | #+END_SRC 115 | ** Create a PBX 116 | If you have existing PBXes and want to provide them with SIP trunks, create a "connectivity" doc. 117 | Be sure any DIDs you add here have been added in the above method (or similar). 118 | #+BEGIN_SRC bash 119 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 120 | -d '{"data":{"account":{"auth_realm":"{ACCOUNT_SIP_REALM}"},"servers":[{"DIDs":{"+12125554321":{}},"options":{"inbound_format":"e164"},"auth":{"auth_method":"password","auth_user":"{USERNAME}","auth_password":"{PASSWORD}"}}]}}' 121 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/connectivity 122 | #+END_SRC 123 | -------------------------------------------------------------------------------- /doc/install/configure_kazoo.md: -------------------------------------------------------------------------------- 1 | # Configuring Kazoo 2 | 3 | This guide assumes you've installed Kazoo via one of the supported methods and are now ready to create devices, users, carriers, etc. 4 | 5 | ## API Basics 6 | 7 | Kazoo requires an auth-token for most API usage. You can create a token via a number of ways but we'll just use the username/password we created in the installation guide. 8 | 9 | ```bash 10 | # User/Pass credentials hash 11 | echo -n "{USERNAME}:{PASSWORD}" | md5sum 12 | {MD5_HASH} - 13 | 14 | # Copy the {MD5_HASH} and create an Auth Token 15 | curl -v -X PUT -H "content-type:application/json" \ 16 | -d '{"data":{"credentials":"{MD5_HASH}","account_name":"{ACCOUNT_NAME}"}}' \ 17 | http://ip.add.re.ss:8000/v2/user_auth | python -mjson.tool 18 | 19 | # Export the "auth_token" and "account_id" for easy use in later API requests 20 | export AUTH_TOKEN="{AUTH_TOKEN}" 21 | export ACCOUNT_ID="{ACCOUNT_ID}" 22 | ``` 23 | 24 | Now your shell will have an auth token and account id to use (please export the real values and not the {…} placeholders. 25 | 26 | ## Create a device 27 | 28 | ### Via API 29 | 30 | ```bash 31 | # Create a base device 32 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 33 | -d '{"data":{"name":"Device1"}}' \ 34 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/devices | python -mjson.tool 35 | 36 | # capture the "id" of the device 37 | export DEVICE_ID="{DEVICE_ID}" 38 | 39 | # Add a terrible username and password 40 | curl -X PATCH -H "X-Auth-Token: $AUTH_TOKEN" \ 41 | -d '{"data":{"sip":{"username":"device_1","password":"password_1"}}}' \ 42 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/devices/$DEVICE_ID | python -mjson.tool 43 | ``` 44 | 45 | Using the realm of the account, you should now be able to register a phone using the credentials created. 46 | 47 | ### Via MonsterUI 48 | 49 | Use SmartPBX - Screenshots welcomed 50 | 51 | ## Create a callflow for the device 52 | 53 | ### Via API 54 | 55 | ```bash 56 | # create a callflow for extension 1001 to ring the device 57 | # note: we need to escape the quotes to use $DEVICE_ID in the JSON data 58 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 59 | -d "{\"data\":{\"name\":\"Device1 Callflow\", \"numbers\":[\"1001\"], \"flow\":{\"module\":\"device\",\"data\":{\"id\":\"$DEVICE_ID\"}}}}" \ 60 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 61 | ``` 62 | 63 | You should now be able to create a second device and call 1001 to ring the first device 64 | 65 | ## Create an outbound carrier 66 | 67 | This assumes you have an upstream carrier that uses username/password to authenticate your calls. 68 | 69 | ```bash 70 | # Create a "resource" representing the carrier 71 | # "rules" is a list of regexes to match numbers for this carrier 72 | # "gateways" is a list of JSON objects representing the gateway(s) to use 73 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 74 | -d '{"data":{"rules":[".{5,}"],"name":"Carrier Foo","gateways":[{"realm":"sip.carrier.com","server":"sip.carrier.com","username":"your_username","password":"your_password","enabled":true}]}}' \ 75 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/resources | python -mjson.tool 76 | 77 | # capture the id of the resource 78 | export RESOURCE_ID="{RESOURCE_ID}" 79 | 80 | # Now create a callflow to use this account resource 81 | # This uses the "no_match" special number 82 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 83 | -d '{"data":{"name":"Offnet Callflow","numbers":["no_match"],"flow":{"module":"resources","data":{"use_local_resources":true}}}}' \ 84 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 85 | ``` 86 | 87 | If you use the regex above, any number 5 digits or more will route to your carrier. 88 | 89 | ## Route numbers to your setup 90 | 91 | Getting numbers to route in Kazoo requires a few steps. This guide will use the defaults in the system (read: mostly US-based numbers) to make this fast. Alternative documentation should be created for handling other areas of the world. 92 | 93 | 94 | ### Add the carrier to the ACLs 95 | 96 | ```bash 97 | sup ecallmgr_maintenance allow_carrier CarrierFoo 1.2.3.4/32 98 | ``` 99 | 100 | You can set the IP as a raw IPv4 IP address or in [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation). 101 | 102 | ### Add a number that you expect your carrier to route to you 103 | 104 | ```bash 105 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 106 | -d '{"data":{}}' \ 107 | "http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/phone_numbers/+15551234567" | python -mjson.tool 108 | 109 | # Activate the number 110 | curl -v -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 111 | -d '{"data":{}}' \ 112 | "http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/phone_numbers/+15551234567/activate" | python -mjson.tool 113 | ``` 114 | 115 | ### Create a callflow for that DID 116 | 117 | You could also amend the callflow created for the first device, adding the number to its "numbers" array. 118 | ```bash 119 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 120 | -d "{\"data\":{\"name\":\"Main Callflow\",\"numbers\":[\"+15551234567\"],\"flow\":{\"module\":\"device\",\"data\":{\"id\":\"$DEVICE_ID\"}}}}" \ 121 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/callflows | python -mjson.tool 122 | ``` 123 | 124 | ## Create a PBX 125 | 126 | If you have existing PBXes and want to provide them with SIP trunks, create a "connectivity" doc. Be sure any DIDs you add here have been added in the above method (or similar). 127 | 128 | ```bash 129 | curl -X PUT -H "X-Auth-Token: $AUTH_TOKEN" \ 130 | -d '{"data":{"account":{"auth_realm":"{ACCOUNT_SIP_REALM}"},"servers":[{"DIDs":{"+12125554321":{}},"options":{"inbound_format":"e164"},"auth":{"auth_method":"password","auth_user":"{USERNAME}","auth_password":"{PASSWORD}"}}]}}' 131 | http://ip.add.re.ss:8000/v2/accounts/$ACCOUNT_ID/connectivity 132 | ``` 133 | -------------------------------------------------------------------------------- /doc/install/downloading.md: -------------------------------------------------------------------------------- 1 | ## Downloading 2 | -------------------------------------------------------------------------------- /doc/install/downloading_packages_securely.md: -------------------------------------------------------------------------------- 1 | # Downloading Packages Securely 2 | 3 | By default, `curl` verifies the certificate used on https://packages.2600hz.com. Unfortunately, most installs don't have the particular certificate chain used here. This requires you to download the packages insecurely. 4 | 5 | If you would like to download the packages without having to pass `-k` or `--insecure` to `curl`, follow these steps: 6 | 7 | Download the "Go Daddy Secure Certificate Authority - G2" chain 8 | ``` 9 | $ curl --output /etc/pki/tls/certs/gdig2.crt https://certificates.godaddy.com/repository/gdig2.crt 10 | ``` 11 | 12 | Add the certificate to nssdb 13 | ``` 14 | # certutil -d sql:/etc/pki/nssdb -A -t "C,C,C" \ 15 | -n "Go Daddy Secure Certificate Authority - G2" \ 16 | -i /etc/pki/tls/certs/gdig2.crt 17 | ``` 18 | 19 | This information was obtained from [this post](https://blog.hqcodeshop.fi/archives/304-Fixing-curl-with-Go-Daddy-Secure-Certificate-Authority-G2-CA-root.html). 20 | -------------------------------------------------------------------------------- /doc/install/install_overview.md: -------------------------------------------------------------------------------- 1 | ## Installation Overview 2 | -------------------------------------------------------------------------------- /doc/install/install_via_cluster_manager.md: -------------------------------------------------------------------------------- 1 | ## Installing via Cluster Manager 2 | -------------------------------------------------------------------------------- /doc/install/install_via_debian.md: -------------------------------------------------------------------------------- 1 | ## Installing via DEB 2 | -------------------------------------------------------------------------------- /doc/install/install_via_iso.md: -------------------------------------------------------------------------------- 1 | ## Install via ISO 2 | 3 | 4 | 5 | **Overview** 6 | 7 | *DISCLAIMER: The Kazoo Single Server ISO is still under development and is not guaranteed to work.* 8 | 9 | If you need a small development or testing **Kazoo** environment, a single server installation may work for you. Keep in mind that only using a single server to host the various **Kazoo** platform software results in limited performance and no redundancy or backups. The **Kazoo** platform was designed to be a scalable, failure resistant platform. 10 | 11 | 12 | ## Installing the ISO 13 | 14 | Prepare your virtual machine or server for installation via ISO. Note that this ISO will overwrite any data on the selected partition/hard drive, so using a virtual machine is preferred. Alternatively, please setup your partitions accordingly. Download the **Kazoo** Single Server ISO here: http://repo.2600hz.com/ISOs/. Make sure to download the latest version. Install the ISO as you would any other **Linux** distribution. The **Kazoo** Single Server ISO is built on **CentOS 6.5**. Remember the root password you set! Once completed, reboot the server and login as the root user. 15 | 16 | When you log in, you will be presented with information regarding your **Kazoo** installation, including the URL for your **Kazoo** web interface and login credentials. By default, this installation method will use a DHCP provided IP. If you are using a static IP, see "Changing IP Settings" below. If you want to change the hostname, you should also do that now. Login to the web interface with the information provided. Please make sure to reset your password! In case you missed it on the previous step, the web interface's URL should look like: (http:///kazoo-ui). You can now start testing your **Kazoo** installation! If you encounter any problems, you can utilize the **Kazoo** **Google Groups: 2600hz-users** and **2600hz-dev**, or visit our public **IRC** channel: **#2600hz** on **Freenode*. 17 | 18 | 19 | ## Using Kazoo 20 | 21 | Were you able to login to the web interface? If so, you have properly installed the Kazoo platform! 22 | 23 | **Changing IP Settings** 24 | By default, this installation method will use a DHCP provided IP. If you as OK with this, then you are set! But if you want to use a static IP, do the following: 25 | 26 | 1. Change your network settings to set your static IP. Run the script `/root/kazoo_ip_set.sh` 27 | 28 | 2. Select STATIC, then select the network interface where your static IP is set. The script will automatically change your Kazoo settings to match your statically set IP. Do you want to use DHCP, but want Kazoo bound on a different network interface? Run the script `/root/kazoo_ip_set.sh` 29 | 30 | 3. Select DHCP, then select the network interface you want Kazoo bound to. The script will automatically change your **Kazoo** settings to bind to the network interface you chose. 31 | 32 | 33 | ## Changing Hostname 34 | 35 | Want to change your hostname? Note that **BigCouch** uses the hostname as a location pointer to where it stores data. For this reason, you will have to wipe out all data in the **Bigcouch** database when you change hostnames. You should probably change hostnames as soon as possible! (It is possible to change hostnames while saving data, but this is out of scope of this simple ISO guide.) Run the script `/root/kazoo_hostname_set.sh`. Follow the script's instructions. Make sure your hostname is fully qualified, this is a requirement for the **Kazoo** platform. The script will change your hostname and update **Kazoo** settings to match. 36 | 37 | 38 | ## Advanced 39 | 40 | By default, this **Kazoo** installation will only be able to utilize local extensions and calling between them. If you want to be able to use real phone numbers and making/receiving calls to/from the outside world, you will need to add your carrier(s) information to the database. 41 | 42 | Note: To access the **BigCouch** database user interface, visit (http://:5984/_utils). For security purposes, this port is blocked by default, so you will either need to forward that port, or temporarily disable iptables. 43 | 44 | -------------------------------------------------------------------------------- /doc/install/minimum_requirements.md: -------------------------------------------------------------------------------- 1 | # Minimum Requirements 2 | 3 | 4 | 5 | ## Minimal Computational Resources 6 | 7 | 8 | **Kazoo** is designed to be as flexible as possible on what platforms it runs on. Realistically, you would not want to rely on an embedded device with 256MB of RAM to handle 1,000,000 calls per second. That said, with modest hardware **Kazoo** will run just fine for small to medium sized businesses, and scaling to larger capacity servers is relatively seamless and painless. 9 | 10 | 11 | ## Virtualized or not? 12 | 13 | Setting up a **Rackspace**, **Amazon**, **Synapse Global**, or other hosted provider is more than acceptable, especially when starting out. As demands increase, moving the **FreeSWITCH** components onto dedicated hardware (or adding more virtualized nodes) is generally enough to increase performance and capacity. Whether you use a virtualized service, dedicated hardware in a datacenter, or a server on your intranet, **Kazoo** will install and work. You can also setup hybrid clusters where some clients are hosted in your "cloud" while others are hosted on site (replicating and failing over to the "cloud" if the premise server goes down). Since joining a **Kazoo** cluster is easy, the configurations possible are flexible and powerful. 14 | 15 | 16 | ## Operating System Recommendations 17 | 18 | It is strongly recommended that **Kazoo** be deployed on the **Linux** distribution **CentOS**, version 6.4 or 6.5. Since **FreeSWITCH** is a large influence on **Kazoo**, and they recommend using **CentOS**; we have a similar recommendation. Also note that this is the OS of choice for **2600hz**'s production deployments. As time goes on, we will be testing other platforms with **Kazoo** and updating this page with recommendations. However, any operating system that **Erlang** or **FreeSWITCH** run on should suffice. 19 | 20 | 21 | ## Preparing Computational and Operating System Resources 22 | 23 | A fully qualified domain name is REQUIRED 24 | 25 | **Kazoo** relies heavily on your server hostnames being fully qualified domain names (or FQDN). Servers are usually given a short-name, such as "bob", by admins to make it easier to refer to and manage. Fully qualified domain names are comprised of the servers name as well as the domain to which it belongs, such as "bob.example.com". Your system may not be configured to use the FQDN if you did not expressly do so. To find out if your system is properly configured try running the following command: 26 | ``` 27 | [root@bob ~]$ hostname -f 28 | bob.example.com 29 | ``` 30 | If running running the command above results in both the servers name and the domain it belongs to (basically "bob.something") skip to the next section. 31 | 32 | To configure your system with a FQDN Hostname you can follow these steps: 33 | 34 | We will modify the systems "hosts" file such that it will be able to understand a FQDN (such as "bob.example.com") belongs to an IP address. The hosts file can be found at `/etc/hosts`. 35 | 36 | If you have a DNS server for the domain, say "example.com" rather than modify the hosts file you could add an A record "bob.example.com" with the servers IP. This will make it easier to manage the cluster if the IP addresses of the equipment is expect to change. However, if you use a DNS service (rather than the hosts file) to resolve the FQDN of the servers in your cluster you need to ensure it is 100% available! DNS failure in this configuration will disrupt voice service. 37 | 38 | -------------------------------------------------------------------------------- /doc/install/planning_zones.md: -------------------------------------------------------------------------------- 1 | ## Planning out zones 2 | 3 | -------------------------------------------------------------------------------- /doc/intro/advanced-concepts.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Dedicated Cluster Guide 2 | 3 | 4 | 5 | **This is our core open-source product and the basis for our company. Everything listed here is effectively open-sourced:** 6 | 7 | 1. Cluster Deployment 8 |   9 | 2. Configuration 10 | 11 | 3. Maintenance 12 | 13 | 4. Troubleshooting 14 |   15 | -------------------------------------------------------------------------------- /doc/intro/build-own.md: -------------------------------------------------------------------------------- 1 | ## Build Your Own Cloud Platform 2 | 3 | 4 | 5 | This section covers how to build your own cloud platform on your own hardware or infrastructure. 6 |   7 | -------------------------------------------------------------------------------- /doc/intro/build_system.md: -------------------------------------------------------------------------------- 1 | ## Build System 2 | 3 | 4 | 5 | We may be revising the build system to meet the demands of additional use cases. This document is a work-in-progress. As the project has grown, more use-cases have evolved. Here are some we've learned about: 6 | 7 | 1. Desire to install on **Debian**, **CentOS**, **FreeBSD**, **Windows** 8 | 2. Desire to install on networks and servers which are 100% secure and private WAN 9 | 3. Desire to host all packages in a separate, non-public repository 10 | 4. Desire to have all dependencies packaged on a single CD or installation disk 11 | 5. Desire to be able to provide single-server upgrade procedures and installation media for remote sites 12 | 13 | 14 | ## Customer Use Cases Include: 15 | 16 | 1. Internet Telephony Service Providers 17 | 2. Fire, Police and Military and Secure Installations With Disparate Sites 18 | 3. Health Care Industries with HIPAA compliance concerns 19 | 4. Installations who intend to be 100% self-sufficient and run everything on a private WAN for security 20 | 5. Installations where the logic and database servers are on non-public IPs and do not allow public internet access 21 | 22 | 23 | ## Current Strategy: 24 | 25 | Bundle everything in the RPM, including all libraries, wherever possible 26 | 27 | 28 | ## Current Problems: 29 | 30 | 1. Effectively forking dependencies. Changes in our repo/dependencies can not be preserved when the dependency is upgraded 31 | 2. Individuals using/working with the project may end up testing/working on old dependency libraries. Upgrades to those libraries may break their work 32 | 3. The current source deployment model proposes a git clone into /opt/kazoo, which results in locally modified config files (`whistle_apps/conf/vm.args` and such) being shown in "**git status**", and they can accidentally be added to the repository. 33 | 34 | 35 | ## New Proposed Standards: 36 | 37 | 1. **Git** repository should only contain sources, and no derivatives (such as beams). This applies to all sources, including third-party dependencies. 38 | 2. The build process should generate all needed beams and other derivatives 39 | 3. The source code does not assume any directory paths, and all the installation paths are defined during the installation (currently, for example: `utils/sup/src/sup.erl` contains "`/opt/kazoo`" hardcoded) 40 | 4. Desirable: strict separation of source directory, installation directory, and configuration directory. This is difficult to achieve within rebar paradigm, as it does not offer installing the application into a target path. 41 | 5. Installed scripts contains direct links to external binaries, such as full path to the Erlang interpreter. This will allow multiple Erlang versions exist on the same server (currently: "erl" from current PATH is used). 42 | 43 | 44 | ## New Proposed Methods: 45 | 46 | 1. GNU Autoconf/Automake – seems to be a quite hardcore task to combine with rebar. One big difference is that automake assumes the source and target paths to be different, while rebar assumes that the source and installation paths are the same. 47 | 48 | 2. Shell scripts for configuration and installation: 49 | 50 | a. Configuration script: takes the installation path (which is equal to CWD), **Erlang** path, configurable options such as cookies and hostnames, and generates the configuration files. These config files are never a part of **Git** repository. The configuration script is executed only once, and aborts if the configuration files already exist. Further modifications are done on configuration files directly. It is desirable to keep the config files outside of Kazoo installation path, then they can be added into their own git repository. 51 | 52 | b. Build script generates all beams and other required derivatives. If any of source files are updated, the corresponding beams are re-built. The build script never modifies configuration files from #1. 53 | 54 | c. Test script validates the current installation 55 | 56 | ## Unresolved architecture issues: 57 | 58 | Do we need to move all third-party dependencies outside of **Kazoo** repository? 59 | 60 | **Pros:** 61 | 62 | Easier to test new versions of third-party libraries. 63 | All original change history in third-party libraries is preserved. 64 | Possibility to use pre-packaged third-party libraries. 65 | A major update in a third-party library does not pollute kazoo repository. 66 | 67 | **Cons:** 68 | 69 | Need to maintain reliable repositories for all dependencies. 70 | More complexity in installation scripts. 71 | Need a script which fetches all third-party dependencies for offline installation. 72 | 73 | -------------------------------------------------------------------------------- /doc/intro/cluster-design.md: -------------------------------------------------------------------------------- 1 | ## Single Server 2 | 3 | 4 | 5 | **Active / Active Cluster Design** 6 | 7 | **Active / Passive Cluster Design** 8 | 9 | **Multi-Zone Design Considerations** 10 |   11 | -------------------------------------------------------------------------------- /doc/intro/component_overview/intro.md: -------------------------------------------------------------------------------- 1 | 2 | ######**OpenSIPS** 3 | 4 | **OpenSIPS** acts as a load balancer for the SIP requests (much the way nginx or HAProxy are used to load balance web requests). We minimize the number of public network interfaces needed to inform clients and carriers of by pointing them to the load balancers (usually two for redundancy). Adding capacity becomes as easy as informing openSIPS of the new switch. 5 | 6 | to the Switch... 7 | 8 | 9 | ######**FreeSWITCH** 10 | 11 | We primarily use **FreeSWITCH** in this layer because of the tight integration we get between **Kazoo** and **FreeSWITCH** via the 12 | `mod_erlang_event` module. As **FreeSWITCH** is already a carrier-grade switch on its own, bringing the clustering features of **Kazoo** on top, you get a truly high-quality cluster of switches on which to build your business. 13 | 14 | to the Control Layer... 15 | 16 | 17 | ######**Control Layer - Kazoo** 18 | 19 | **Kazoo** provides an abstraction layer (among other things) to the underlying switching layer. Application developers can program their applications against the **Kazoo** APIs and know that Kazoo will take care of the details. Application developers also benefit from Kazoo's ability to distribute processing amongst the servers in the switching layer. To the application developer, Kazoo is one logical switch. 20 | 21 | to the Message Bus... 22 | 23 | 24 | ######**RabbitMQ** 25 | 26 | We primarily start and conduct conversations between servers using a standard protocol named AMQP, which is implemented via a program named **RabbitMQ**. While we've had discussions about 27 | faster systems like **ZeroMQ** (theoretically anyway), **RabbitMQ** allows us to keep everything in native Erlang data types, pass things around our software quickly, and cluster **Kazoo** and **WhApp** servers easily. It also implements all the brokers we need out-of-the-box. 28 | 29 | to the Logic... 30 | 31 | 32 | ######**WhApps** 33 | 34 | **WhApps** can control what happens at all stages of a call (even initiate calls of their own). Authentication, routing, in-call applications (like IVRs and voicemail), and more, are all exposed via the **Kazoo** APIs. We provide a set of APIs via a REST interface, implemented as a **WhApp** named **Crossbar**. With **Crossbar**, configuration of PBX functionality and more is exposed. Other **WhApps** included are Registrar (authentication and distributed registration server), **Callflow** (real-time dialplan execution), **MediaMgr** (real-time streaming of MP3 and WAV media files to the switching layer), and **Trunkstore** (trunking platform). 35 | 36 | to the Storage.... 37 | 38 | 39 | ######**BigCouch** 40 | 41 | Its a known secret that **BigCouch** is the magic fairy dust that makes **Kazoo** so reliable. With the ability to replicate data, dynamically adjust the read and write quorums, and a simple-to-use HTTP interface, developing our platform using **BigCouch** as the long term datastore has been a huge win. As important, from an operational perspective, once you understand the knobs and levers to turn to tweak the performance characteristics, **BigCouch** is a breeze to operate and maintain. 42 | -------------------------------------------------------------------------------- /doc/intro/contributing.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | 4 | 5 | ## Code 6 | 7 | xxx 8 | 9 | ## Documentation 10 | 11 | xxx 12 | 13 | ## Reward System 14 | 15 | xxx 16 | -------------------------------------------------------------------------------- /doc/intro/create-template.md: -------------------------------------------------------------------------------- 1 | ## Create A Template 2 | 3 | 4 | 5 | Give your authors a helping hand by using templates in your documentation space. You will need "Space Administrator" permissions to create templates. 6 | 7 | To create a Template: 8 | 9 | 1. Go to 'Space Tools' in the sidebar, select 'Content Tools' and create a new template. 10 | 11 | 2. Click 'Page Layout' and add sections and columns to your page. Add headings and sub-headings as needed. 12 | 13 | 3. Choose 'Instructional Text' from the 'Template' menu and add text that is only visible in the editor. Save your template. 14 | 15 | 4. Your template will become available in the 'Create' dialog for this space. 16 | 17 | 18 | *Useful hint: **Confluence** Administrators can also make templates that are available across your whole **Confluence** instance. 19 | -------------------------------------------------------------------------------- /doc/intro/disabling-servers.md: -------------------------------------------------------------------------------- 1 | ## Disable Servers 2 | 3 | 4 | 5 | Preparing servers for an upgrade requires gracefully disabling them without dropping any calls. 6 | 7 | -------------------------------------------------------------------------------- /doc/intro/distributed-reg.md: -------------------------------------------------------------------------------- 1 | ## Traditional Registration 2 | 3 | 4 | 5 | When a business or hobby first starts out, a single switch is the typical starting point. Clients point their devices to the IP of the switch, registration credentials are stored on the switch, and life is good. Then the switch begins to fill up with clients, to the point where a second server is needed handle the capacity demands. No problem; new clients on the new switch. But what happens when a client on the first switch grows? Migrate them to the new switch? Setup another switch just for them? But now you have to change the configurations on their devices, update carriers that routed DIDs to the first switch, perhaps you customized some features for that client that now have to be migrated as well...in short, it gets unwieldy fast. 6 | 7 | This speaks nothing of the fact that if that server crashes (for a host of reasons, some controllable, some not), all clients on said switch are without service. Even when you have a hot spare or master-master setup, its no guarantee the system will survive a crash. If both servers are at 70% load, when one goes down, that 70% is headed to the other switch. How fast can the ops team spin up a new server? 8 | 9 | 10 | ## A Better Way 11 | 12 | At a general level, the softswitch was not built to handle the distributed case. They route calls, handle codec transcoding (if necessary), but they assume they are the authority on what devices can connect and how to find devices for calls. 13 | **Whistle** has relieved them of that concern (and others). When a registration request is received by the switch, it asks **Whistle** to give it the necessary credentials to verify the device. If successful, the switch lets **Whistle** know; **Whistle** then sends the successful registration information out the any listening WhApps (in our case, `registrar`, which stores them in **BigCouch** for later retrieval). 14 | 15 | Now, when the switch receives a call that needs to be routed to a known device, it just asks **Whistle** for the contact information and sends the SIP traffic on its merry way. The handling switch, however, need not have ever interacted with the device before that point. 16 | 17 | 18 | ## Why Do I Care? 19 | 20 | Because the switch is agnostic to devices, and instead trusts **Whistle** to guide it in routing calls, your clients can register their devices to any of the switches in your cluster, and carriers can send calls destined for your clients to any of the switches as well. Better still, you can put a load balancer in front of your switches, giving your clients two IPs (assume two load balancers for redundancy) to register their devices against. These load balancers keep no history of what device hit which switch last time but distribute the load evenly across your cluster. 21 | 22 | Capacity issues? Add a switch into the cluster behind the load balancers, update the load balancers' configs, and voila, increased capacity without the client or carrier changing a single setting. 23 | 24 | 25 | ## Tangential Benefits 26 | 27 | Because the switch is no longer storing state between calls, performance gains are noticeable as disk activity and memory usage decreases, which increases capacity as more concurrent calls can be loaded on a single switch. Plus, impacts from a switch going down are limited to the current calls (and some switches, namely **FreeSWITCH**, are building functionality to be able to rebuild an in-progress call on a new server). When those calls restart, they are evenly distributed across your remaining cluster, not overwhelming the hapless hot spare or co-master. 28 | 29 | Capacity planning becomes much easier when you can spread load from a single failed switch across 3, 5, 10, or even 100 other switches. As the number of switches increases, your ability to load them with higher and higher sustained call rates without fear of cascading failures grows as well. 30 | -------------------------------------------------------------------------------- /doc/intro/expand-cluster.md: -------------------------------------------------------------------------------- 1 | ## Expand Cluster 2 | 3 | 4 | 5 | How to safely expand the cluster to a new server, datacenter or continent 6 |   7 | -------------------------------------------------------------------------------- /doc/intro/g729-licensing.md: -------------------------------------------------------------------------------- 1 | ##G729 Licensing 2 | 3 | 4 | 5 | Installing and managing G729 licenses 6 | -------------------------------------------------------------------------------- /doc/intro/get_help.md: -------------------------------------------------------------------------------- 1 | ## Get some help! 2 | -------------------------------------------------------------------------------- /doc/intro/getting-started.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | 4 | 5 | ## Create a page 6 | 7 | Click 'Create' and select 'Blank' Page to create your first page. New pages are created as children of the page you are currently viewing. 8 | 9 | 10 | ## Add to your page 11 | 12 | Click 'Edit' to enter the **Confluence** editor and use the page layouts feature to structure your content using sections and columns. Use headings to format your text and drag and drop images into your page to provide visual interest. Click 'Insert' and select 'Other 13 | Macros' to add macros for navigation, special formatting and other media: 14 | 15 | 1. Create a page 16 | 17 | 2. Add to your page 18 | 19 | 3. Organise your pages 20 | 21 | 4. Change the page order 22 | 23 | 5. Add labels 24 | 25 | 6. Make templates 26 | 27 | 28 | ## Organise your pages 29 | 30 | Here are some tips for organising your content: 31 | 32 | Change the page order.The sidebar on the left displays your pages in a hierarchy. If you have 'Space Administrator' permissions you can click 'Space Tools' to reorder Pages to move pages around. 33 | 34 | 35 | ## Add labels 36 | 37 | Labels help keep pages organised and make it easier for you to find the information you need. Click 'Labels' at the bottom of a page to 38 | add or edit. The Related pages section on this page uses labels too! 39 | 40 | 41 | ## Make templates 42 | 43 | Standardise and speed up the page creation process with templates. You can create and format a template with page layouts, standard headings and instructional text for hints and guidelines. Check out our sample page on making a template. 44 | 45 | 46 | -------------------------------------------------------------------------------- /doc/intro/happy-programmer.md: -------------------------------------------------------------------------------- 1 | ## Happy programmers write happy code 2 | 3 | 4 | 5 | Summarized thus: 6 | 7 | *Only program the happy case, what the specification says the task is supposed to do ~ Joe Armstrong* 8 | 9 | Did you know that **Erlang** programmers would make mighty fine actors (or at least actor managers)? Besides the fact that they deal with 10 | actors all day long (**Erlang** processes), they also take on the character of the process. When branching (using case, if, or function clauses), the executing process needs to determine "Do I know how to handle an error here?" 11 | 12 | A common idiom is to return `{ok, Result}` or `{error, Error}` when calling another module to do something for you. If you, the calling process, don't know what to do with `{error, Error}`, why branch? Just match `{ok, Result}` and continue programming the happy case. Let some one else (a supervisor or whomever spawned you) clean up the mess. That said, if you know how to handle the error, you should certainly branch and handle it appropriately. It's not always this cut and dry, of course, but asking, "Do I know how to handle an error here?" is a good start to deciding whether to wrap that function call in a case statement or not. If this is a long-running server, failing intentionally probably isn't a wise course of action. However, if it's a worker process spawned by the server, it's likely okay to not handle the error. By keeping your workers' outlooks happy, you simplify and reduce the amount of code in those workers, and hopefully make it easier to reason about them when determining if they are meeting their specification. 13 | -------------------------------------------------------------------------------- /doc/intro/inbound-call-issue.md: -------------------------------------------------------------------------------- 1 | ## Inbound Call Issues 2 | 3 | 4 | 5 | When a customer complains an inbound number isn't working: 6 | 7 | 1. Call the number yourself, see if it rings from a system phone. 8 | 9 | 2. Call the number from your cell phone or an out side line. See if it rings. If it works outside but not inside... 10 | 11 | 3. If it works inside but not outside... 12 |   13 | 4. If the above both work: 14 | 15 |   16 | -------------------------------------------------------------------------------- /doc/intro/kazoo-media.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Media 2 | 3 | 4 | 5 | ## Streaming Audio (mp3 and wav) to FreeSWITCH from Erlang 6 | 7 | One of the biggest challenges to overcome with distributing **FreeSWITCH** servers inside your cluster is getting them access to the same media files, both system and user-generated (like voicemails and custom prompts). Replicating (via rsync or similar) files amongst the **FreeSWITCH** servers works at a small scale, but increase over a few servers and the operational requirements accelerate. 8 | 9 | 10 | ## Challenges 11 | 12 | How to ensure each server has the same set of media files? 13 | 14 | How to ensure each server has the same version of media files? 15 | 16 | How to ensure each server received a copy of a media file? 17 | 18 | How to ensure a newly added server gets all the updated media? 19 | 20 | How to ensure a newly added server gets any new media? 21 | 22 | Will each server realistically be able to store all media across the system? 23 | 24 | 25 | ## To The Cloud Stateless Switch 26 | 27 | If a switch or subset of switches are responsible for storing a client's voicemail files, we have a point of failure with respect to that client. With the single switch setup, it is an obvious point of failure. As you increase the number of switches storing media, the operational costs begin to increase (while the likelihood of losing those files decreases). However, the client is still limited in which switch they are able to hit and retrieve their media. 28 | 29 | A guiding decision we've made in **Whistle** is that the underlying switching servers should be as "dumb" as possible with respect to the decision making process. Included in this decision is the storage of client media. As seen above, the job of synchronizing media across the cluster is difficult in the switching layer. Whistle instead pushes the responsibility of handling media up to the **WhApp** layer. As long as a switch is managed by **Whistle**, and Whistle is connected via messaging bus to a **WhApp** that handles media distribution, all switches have access to the same media. 30 | 31 | The biggest enablers of this strategy, from the switch's perspective, are two **FreeSWITCH** modules: `mod_shout` and `mod_shell_stream`. `mod_shout` provides us with an entry point for both streaming mp3 media into a call, as well as streaming recordings from **FreeSWITCH** to Whistle (more on this later). `mod_shell_stream`, as the wiki states, allows you to "stream audio from an arbitrary shell command". Combining this with a simple cURL+sox script, we now have the building blocks to stream all audio to/from **Whistle/WhApps**. 32 | 33 | 34 | ## Media Manager 35 | 36 | Included with **Whistle** is a media management **WhApp** named `media_mgr`. 37 | 38 | 39 | ## Sending Audio to FreeSWITCH 40 | 41 | When **Whistle** encounters a dialplan action requiring media, it makes a request to the message bus asking for an accessible URL from which to stream the media. `media_mgr` receives the request and does a lookup in the data-store for the requested media. If found, a stream process is created and give the data necessary to stream the chosen media contents. The media type (WAV or MP3) determines the type of stream (HTTP or SHOUT, respectively). The stream process crafts a URL with the port it will be listening on and sends the response back to the **Whistle** process handling the call. 42 | 43 | **Whistle** checks the protocol of the URL received to determine how to send the stream to **FreeSWITCH**. If the protocol is http:// (WAV), Whistle knows that mod_shell_stream should be invoked and creates the play command to do so. If the URL is shout://, **Whistle** passes it along to **FreeSWITCH**; **FreeSWITCH** then passes control to `mod_shout` to pull the actual contents down. 44 | `mod_shell_stream` script. To invoke `mod_shell_stream`, **Whistle** passes a command to **FreeSWITCH** similar to: 45 | 46 | `shell_stream:///path/to/fetch_remote_audio.sh http://mediamgr.url.com/stream.wav` 47 | 48 | The `fetch_remove_audio.sh` script makes an HTTP request to the stream URL, and pipes the body (the WAV contents) to sox, which in turn sends the data into **FreeSWITCH**. 49 | 50 | 51 | ## Recording Audio 52 | 53 | `mod_shout` also allows us to stream a recording to a SHOUT server rather than to the local disk, critical when a voicemail is being left. 54 | Our voicemail is actually a callflow module and sends a MediaName to **Whistle** to identify the recording for later use in the call. **Whistle**, when executing the record instruction, creates a SHOUT server process and sends the URL along with the record command, something along the lines of: 55 | 56 | `shout://foo:bar@whistle.server.com:39266/fs_02688d5d2b37267a2e9496d156cb52dd.mp3 120 500 5` 57 | 58 | Yes, the `foo:bar` is actually used. `mod_shout` uses basic HTTP auth to connect to the **Whistle** SHOUT server, expects an appropriate response, and then streams the recording to **Whistle**. **Whistle** stores this recording to its local disk. If the user on the call wants to review the recording, **Whistle** actually sets up a SHOUT streaming server, much like `media_mgr` did, and streams the recording back into **FreeSWITCH**. When the **WhApp** controlling the call issues a store command, in this case passing the **CouchDB** URL with attachment to stream into, Whistle reads the file off its local disk and streams away as normal. Once the phone call ends, the recorded media will be streamed via `media_mgr` (say when a user checks their voicemail). 59 | -------------------------------------------------------------------------------- /doc/intro/load-failure.md: -------------------------------------------------------------------------------- 1 | ## Load Failure 2 | 3 | 4 | 5 | Border systems down 6 | 7 | Database issues 8 | 9 | Timeouts in Erlang Event 10 | 11 | FreeSWITCH stability 12 | 13 | Flapping 14 | 15 | Jitter 16 | 17 | Data corruption 18 | 19 | Data growth 20 | 21 | When to spin up new systems 22 | -------------------------------------------------------------------------------- /doc/intro/milliwatt.md: -------------------------------------------------------------------------------- 1 | ## Milliwatt 2 | 3 | 4 | 5 | Milliwat allows automatic echo testing and tone stream. 6 | -------------------------------------------------------------------------------- /doc/intro/number-lifecycle.md: -------------------------------------------------------------------------------- 1 | ## Number Lifecycle 2 | 3 | 4 | 5 | This document describes the lifecycle of direct inward dialing (DID) numbers. 6 | 7 | 8 | ## Figure Discovery 9 | 10 | 1. This is an internal number state used to temporarily hold information regarding numbers users can acquire from the system owner carriers. 11 | 12 | 2. Populated (usually) by API requests to the carriers.   13 | 14 | 3. Does not participate in number hunts. 15 | 16 | 4. No accounts have authority to transition to/from this state. 17 | 18 | 5. There are no public fields for discovery numbers. 19 | 20 | 6. Any account can transition an available number to reserved. 21 | 22 | 23 | ## Port In 24 | 25 | 1. This is a temporary state for a newly added number by an account via the porting API. 26 | 27 | 2. Automatically transitions to `in_service` the first time hunted by the system (first call to it). Does not participate in the off-net number hunt, meaning it is only used to route inbound and will not keep calls on-net. No accounts have authority to transition to/from this state. 28 | 29 | 3. Any account can create a `port_in` number if it does not exist in the system. 30 | 31 | 4. The public fields of a `port_in`number can be managed by the assigned account or an ancestor of the assigned account. 32 | 33 | 5. Only numbers in the `port_in` state allow the porting documents to be managed. 34 | 35 | 6. These numbers can only exist in the following states: `in_service`, `port_out`, and `disconnected`. 36 | 37 | 7. None of these transitions can be preformed by accounts. 38 | 39 | 40 | ## Available 41 | 42 | 1. This is a number that has been routed to the cluster but has no account assignment. Any account can transition these numbers to `reserved` or `in_service` if they wish to obtain control of it.   43 | 44 | 2. Does not participate in number hunts. Any account can transition an `available` number to `reserved`. 45 | 46 | 3. Any account can transition an `available` number to `in_service` if it is not of type `knm_local`. 47 | 48 | 4. There are no public fields for available numbers, but they can be created during a transition. 49 |   50 | 51 | ## Reserved 52 | 53 | 1. This is a number that an account has acquired for themselves or their children.   54 | 55 | 2. Some accounts can create reserved numbers. 56 | 57 | 3. Does not participate in number hunts. 58 | 59 | 4. Numbers coming from the discovery state will be acquired via the carrier modules. 60 | 61 | 5. An account can transition a reserved number to `in_service` if one of the following criteria is met: 62 | 63 | a. The requesting account is the same as the assigned account. 64 | 65 | b. The requesting account is an ancestor of the assigned account. 66 | 67 | c. The requesting account is an descendant of the assigned account. 68 | 69 | d. The new account assignment is the same or a descendant of the current assignment. 70 | 71 | 72 | 6. An account can reserve a 'reserved number' if one of the following criteria is met: 73 | 74 | a. The requesting account is an ancestor of the assigned account. 75 | 76 | b. The requesting account is an descendant of the assigned account. 77 | 78 | c. The new account assignment is a descendant of the current assignment. 79 | 80 | d. When a number is reserved the new assignment is added to the history of assignments. 81 | 82 | 7. A flagged account can create a reserved number of type `knm_local`. These numbers can only be acquired and managed by the creating account or a descendant. 83 | 84 | 8. E911 assignments will be maintained, if present. 85 | 86 | 9. The public fields of a reserved number can be managed by the assigned account or an ancestor of the assigned account. 87 |   88 | 89 | ## In Service 90 | 91 | This is a number that belongs to an account. 92 | 93 | *Participates in number hunts.* 94 | 95 | An `in_service` number can be transitioned to `reserved` if the requesting account is, or an ancestor of, the assigned account for the number. 96 | 97 | E911 assignments will be maintained, if present. 98 | 99 | The public fields of an `in_service` number can be managed by the assigned account or an ancestor of the assigned account. 100 | 101 | 102 | ## Released 103 | 104 | This is an aging state that numbers added to the system via discovery remain for a period of time till moving back to available to start the cycle again. 105 | 106 | *Does not participate in number hunts.* 107 | 108 | If a `reserved` number is released: 109 | 110 | With an assignment history it remains `reserved` but is re-assigned to the previously assigned account. 111 | 112 | Without an assignment history and is of type `knm_local` it transitions to disconnected. 113 | 114 | Any other type completes the transition to released. 115 | 116 | If an `in_service` number is released: 117 | 118 | With an assignment history it becomes `reserved`and is re-assigned to the previously assigned account 119 | 120 | Without an assignment history and is of type `knm_local` it transitions to `disconnected` 121 | 122 | Any other type completes the transition to `released` 123 | 124 | The public fields of an released number are removed. 125 | 126 | Released numbers can be transitioned to available by the system or system admin accounts only. 127 | 128 | E911 is disconnected, if present. 129 |   130 | 131 | ## Port Out 132 | 133 | This is an administrative step to releasing numbers. It operates like 'Port In' except that inbound requests do not move it to In Service. 134 | 135 | 136 | ## Disconnected 137 | This is an internal number state, and marks the number for a permanent move from the active datastore to the archive. 138 |   139 | -------------------------------------------------------------------------------- /doc/intro/read_me_first.md: -------------------------------------------------------------------------------- 1 | ## READ ME FIRST 2 | 3 | 4 | 5 | **Kazoo** is a sophisticated piece of software that allows for large-scale clustering across a WAN. While it is intended to be simple, at times, it can have a high learning curve. 6 | 7 | 8 | ## Required Skills 9 | 10 | You should seriously consider whether you're ready to undertake deploying and managing the entire infrastructure from the start. We now recommend starting with our hosted platform. You can always move to your own software and equipment at any time, without losing data or incurring downtime. 11 | 12 | There are two main areas you are going to need to learn when you dive into **Kazoo**: 13 | 1. How to Setup Customers 14 | * via the GUIs 15 | * via the APIs 16 | 2. How to Manage the Back-end 17 | * FreeSWITCH 18 | * BigCouch 19 | * Kamailio 20 | * Kazoo 21 | * eCallMgr 22 | * RabbitMQ 23 | 3. Client DNS Mapping 24 | 25 | The **Kazoo UI** & APIs may take you a few weeks to become familiar with. The back-end will probably take a bit longer. 26 | 27 | We've structured things so you can start out on our hosted platform. It's inexpensive and it lets you get to know the system prior to diving into the deep end and trying to run it all yourself. There's enough to learn in the system as-is! You can customize the entire experience for your customers while using our hardened hosted system and you can move your customers seamlessly when you are ready to manage your own infrastructure. 28 | 29 | 30 | ## Required Knowledge and Facilities 31 | 32 | **Consider Hosting With Us** 33 | 34 | If you're truly ready to dive into running your own cluster, please be sure you understand what's involved. You must: 35 | 36 | Have a highly reliable datacenter with extremely consistent network connectivity. Too many people make the mistake of assuming - "I've used them for web hosting for years" - this isn't web hosting! Phone calls require uninterrupted connectivity. 37 | 38 | Have a good understanding of **Linux**, including debugging firewall and network connectivity 39 | 40 | Have some understanding of databases and database clusters 41 | 42 | Helpful to have at least played with **BigCouch**, **Erlang**, **FreeSWITCH** and/or **Kamailio** a little 43 | 44 | If you can genuinely meet all the requirements above, the sky is the limit on what you can build! Enjoy. 45 | 46 | -------------------------------------------------------------------------------- /doc/intro/resources.md: -------------------------------------------------------------------------------- 1 | Resources 2 | 3 | Packages 4 | 5 | GitSwitch 6 | 7 | Jenkins/Hudson? 8 | 9 | CI 10 | 11 | Travis 12 | 13 | -------------------------------------------------------------------------------- /doc/intro/startup-sequence.md: -------------------------------------------------------------------------------- 1 | Understanding the startup sequence can be essential in debugging. 2 |   3 | -------------------------------------------------------------------------------- /doc/intro/storefwd.md: -------------------------------------------------------------------------------- 1 | ## Store and Forward (T.37) 2 | 3 |   4 | 5 | ##Ways to transmit a Fax over IP: 6 | 7 | 8 | ## PCMU 9 |   10 | Plain old audio, uncompressed (64k) regular phone line. PCMU is OK if you can guarantee the audio signal is consistent around 200-300ms latency. T.38 - At least two sides must understand T.38 to use it: 11 | 12 | Negotiation was be successful between the two sides. Who negotiates first? 13 | 14 | v0, v1, v2, v3, ECM, v17 - Max Speed (14400/9600) 15 |   16 | Redundancy Feature - FAX - ATA - CLOUD - v1, v2, v3 17 |   18 | 19 | ## STEPS FOR T.38 TO WORK: 20 | 21 | Two devices must be able to hear each other initially via voice (to detect the tones)  22 | Best way to test: CALL THE FAX MACHINE. Also, have them call you from the fax machine if possible. CLUE: No t38 message in the logs AND hang up within 15-30 seconds. CATCH: Many fax machines don't answer for 5 rings, and your calling timeout might be too long. You'll see NO_ANSWER in the FreeSWITCH log. Two devices must be able to negotiate T38 either with each other or with the upstream phone company (You won't know who's on the other side, and that's OK). You will see a log entry in the FS log that a negotiation of T38 was attempted. You will then see a reply from the other side of either Not Acceptable Here / Incompatible Destination OR OK. In addition, the OK will come back with the confirmed parameters 23 | 24 | 25 | ## For Testing: 26 | 27 | 1. Need One (1) Physical Fax that can Send/Receive 28 | 2. Need One Virtual or Physical Fax that can Send /Receive 29 | 3. PATIENCE! 30 | 4. Test PCMU. Should work over fast connection but may not always - The most optimized services should work, so for example RingCentral 31 | Our Fax to Email (server) Service. Probably won't work to a real fax machine 32 | 5. Test Two-way Audio 33 | 6. Test Long/Complex Faxes 34 | 7. Test Short/Simple Faxes 35 | 8. Test Two Model ATAs :-) and no more then two models. 36 | 9. Test Two Fax Machines. 37 | 10. Test error conditions and how OUR software handles them. 38 | 11. Inspect quality for lost rows/lines. 39 | 12. Someday test store forward. 40 | 13. Test Failback abilities on our side. 41 |   42 | -------------------------------------------------------------------------------- /doc/kazoo/call-rating.md: -------------------------------------------------------------------------------- 1 | ## CALL RATING 2 | 3 | 4 | 5 | **Kazoo** provides a basic facility to load a rate-deck into the system for the purpose of rating each call. The rate is only utilized if 6 | the system determines the caller does not have any flat-rate services available for the number being dialed. 7 | 8 | 1. How to load the rate deck 9 | 10 | 2. How to test the rate deck (if possible) 11 | 12 | 3. How to check our costs vs. retail costs 13 | 14 | 4. How to make sure the rating module is loaded / running / being utilized 15 | 16 | 5. Info dump to later be cleaned up 17 | 18 | 19 | **Call Rating** is handled by the **HotOrNot** application. When it starts up for the first time, it will create a database called "Ratedeck" and load an initial rate document for US-domestic calls. That document looks like: 20 | 21 | ``` 22 | { _id : US-1, direction: [ inbound, outbound ], 23 | pvt_internal_rate_cost : 0.01, 24 | iso_country_code: US, options: [ ], 25 | prefix: 1, 26 | rate_cost: 0.02, 27 | rate_increment: 60, 28 | rate_minimum: 60, 29 | rate_name: US-1, 30 | rate_surcharge: 0.00, 31 | routes: [^0111(\\d{10})$, ^\\+1(\\d{10})$], 32 | pvt_type: rate 33 | 34 | } 35 | ``` 36 | 37 | ## Create your own rate document 38 | 39 | `_id` is arbitrary, but we recommend using `iso_country_code` + prefix direction determines whether to apply the rate to an inbound or 40 | outbound call (or both). 41 | 42 | `pvt_internal_rate_cost` // is what you are charged by your upstream carrier (optional) 43 | 44 | `iso_country_code` // what country this rate is applicable. 45 | 46 | `options` // what features does this rate apply to. You might put `[Fax]` for a rate, and if a fax machine is configured and has the same 47 | flag in its settings, this rate will be used. 48 | 49 | `rate_cost` // how much the call will cost, per rate_increment 50 | 51 | `rate_increment` // how many seconds the `rate_cost` applies to. So $0.02 for `rate_cost` and 60 for `rate_increment` means 2 cents per minute. 52 | 53 | `rate_minimum` // how many seconds to bill for at a minimum. If a call lasts 30 seconds and `rate_minimum` is 60, bill it as a 60-second 54 | call. 55 | 56 | `rate_name` // friendly name, eventually will be exposed in the GUI 57 | 58 | `rate_surcharge` // flat rate to charge for connecting the call. 59 | 60 | `routes` // list of regex to apply to the dialed DID to determine if this rate is eligible to be applied to the call. 61 | 62 | `pvt_type` // 63 | 64 | `rate` // 65 | 66 | 67 | ## Other info 68 | 69 | The rate chosen is based on the prefix length. So if I have two rates with routes that match the dialed DID, one with prefix '1' and one with prefix '123', the rate for prefix '123' will be applied. The rate information is placed in the channel variables and will appear in the call's **CDR** data under the `Custom-Channel-Vars` field. 70 | -------------------------------------------------------------------------------- /doc/kazoo/ecallmgr-cmd.md: -------------------------------------------------------------------------------- 1 | ## Kazoo ECallMgr 2 | 3 | 4 | 5 | Also, you are the only person receiving `low_balance` (code change).  I have also stopped the `jonny5` reconciler: 6 |          7 | `sup jonny5_maintenance stop_reconciler` 8 |   9 | 10 | ## The following functions are now available for conferences:       11 | 12 | ``` 13 | sup -necallmgr ecallmgr_maintenance conference_summary   14 | 15 | sup -necallmgr ecallmgr_maintenance conference_details 16 | ``` 17 | 18 | 19 | ## To fix any errors there is also: 20 | ```         21 | sup -necallmgr ecallmgr_maintenance sync_conferences  22 | 23 | sup -necallmgr ecallmgr_maintenance sync_conferences freeswitch@server.com 24 | 25 | ``` 26 | ## This brings the complete list to:        27 | ``` 28 | sup -necallmgr ecallmgr_maintenance add_fs_node freeswitch@server.com       29 | 30 | sup -necallmgr ecallmgr_maintenance remove_fs_node freeswitch@server.com         31 | 32 | sup -necallmgr ecallmgr_maintenance node_summary 33 | 34 | sup -necallmgr ecallmgr_maintenance node_details 35 | 36 | sup -necallmgr ecallmgr_maintenance node_details freeswitch@server.com        37 | 38 | sup -necallmgr ecallmgr_maintenance authz_summary 39 | 40 | sup -necallmgr ecallmgr_maintenance channel_summary 41 | 42 | sup -necallmgr ecallmgr_maintenance channel_summary freeswitch@server.com 43 | 44 | sup -necallmgr ecallmgr_maintenance channel_details 45 | 46 | sup -necallmgr ecallmgr_maintenance channel_details 1913218612@192.168.1.133 47 | 48 | sup -necallmgr ecallmgr_maintenance  sync_channels 49 | 50 | sup -necallmgr ecallmgr_maintenance sync_channels freeswitch@server.com 51 | 52 | sup -necallmgr ecallmgr_maintenance conference_summary 53 | 54 | sup -necallmgr ecallmgr_maintenance conference_summary freeswitch@server.com 55 | 56 | sup -necallmgr ecallmgr_maintenance conference_details 57 | 58 | sup -necallmgr ecallmgr_maintenance conference_details b57f3183a8f11ba31b3b5163057e2adc 59 | 60 | sup -necallmgr ecallmgr_maintenance sync_conferences 61 | 62 | sup -necallmgr ecallmgr_maintenance sync_conferences freeswitch@server.com 63 | 64 | sup -necallmgr ecallmgr_maintenance flush_node_channels freeswitch@server.com 65 | 66 | sup -necallmgr ecallmgr_maintenance flush_node_conferences freeswitch@server.com 67 | 68 | sup -necallmgr ecallmgr_maintenance flush_registrar 69 | 70 | sup -necallmgr ecallmgr_maintenance flush_registrar bea1.sip.2600hz.com 71 | 72 | sup -necallmgr ecallmgr_maintenance flush_registrar 105 bea1.sip.2600hz.com 73 | 74 | sup -necallmgr ecallmgr_maintenance registrar_summary 75 | 76 | sup -necallmgr ecallmgr_maintenance registrar_summary bea1.sip.2600hz.com 77 | 78 | sup -necallmgr ecallmgr_maintenance registrar_details 79 | 80 | sup -necallmgr ecallmgr_maintenance registrar_details bea1.sip.2600hz.com 81 | 82 | sup -necallmgr ecallmgr_maintenance registrar_details 105 bea1.sip.2600hz.com 83 | 84 | sup -necallmgr ecallmgr_maintenance flush_authn 85 | 86 | sup -necallmgr ecallmgr_maintenance flush_util 87 | 88 | ``` 89 | 90 | 91 | 92 | 93 | 94 | 95 |   96 |   97 | -------------------------------------------------------------------------------- /doc/kazoo/enable-cnam.md: -------------------------------------------------------------------------------- 1 | # Kazoo Enable CNAM 2 | 3 | It is possible to utilize **Kazoo** to enable **CNAM** lookups on inbound calls. This allows you to overlay Caller ID in the US with **CNAM** dips from a third-party service. 4 | 5 | ## Supported CNAM Providers 6 | 7 | * Carriers 8 | * Vitelity 9 | * Telnyx 10 | * Providers 11 | * OpenCNAM 12 | * Others possible 13 | 14 | If you provision a number using one of the carriers above, you can enable CNAM features using the [phone numbers](https://docs.2600hz.com/dev/applications/crossbar/doc/phone_numbers/) API. 15 | 16 | ## Setup OpenCNAM (or other providers) 17 | 18 | Kazoo provides a generic HTTP configuration for making CNAM dips. OpenCNAM is known to work and other providers may too, provided they return the Caller ID Name as the HTTP response body in plain text. This is handy when you provision numbers outside of Kazoo and import them. 19 | 20 | To setup the Inbound CNAM dip, use the [system config](https://docs.2600hz.com/dev/applications/crossbar/doc/system_configs/) API to create/edit the [`stepswitch.cnam`](https://github.com/2600hz/kazoo/blob/master/applications/crossbar/priv/couchdb/schemas/system_config.stepswitch.cnam.json): 21 | 22 | ```bash 23 | curl -v -X POST \ 24 | -H "X-Auth-Token: {AUTH_TOKEN}" \ 25 | 'http://{SERVER}:8000/v2/system_configs/stepswitch.cnam' \ 26 | -d '{"data":{"default":{...}}}' 27 | ``` 28 | 29 | Check the [JSON schema]((https://github.com/2600hz/kazoo/blob/master/applications/crossbar/priv/couchdb/schemas/system_config.stepswitch.cnam.json)) for the necessary fields to set in the `{...}` portion. 30 | -------------------------------------------------------------------------------- /doc/kazoo/home-page.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Home Page 2 | 3 | 4 | 5 | Welcome to **Kazoo**, the core open-source product from **2600hz**. This documentation exists to help you install and manage your own dedicated **Kazoo** cluster. 6 | 7 | Before You Begin: 8 | 9 | Who This Is For - **PLEASE READ!**: 10 |   11 | Architectural Review 12 | 13 | Design Concepts 14 | 15 | Cluster Design Patterns 16 |   17 | Cluster Deployment 18 |   19 | Configuration 20 |   21 | Maintenance 22 | 23 | Troubleshooting 24 |   25 | -------------------------------------------------------------------------------- /doc/kazoo/kazoo-debug.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Debug 2 | 3 | 4 | 5 | To be able to debug your setup you need to check the logs. The command tail `-f xxx.log` will open the `logfile` and present a live 6 | running view. 7 |   8 | Essential log files are: 9 | 10 | /var/log/2600hz/kazoo.log 11 | 12 | The main log of the **Kazoo** platform, tells you roughly what is happening on your systems in terms of **Kazoo**. It requires some getting used to, but after that its your best friend. If you need it to show you more details you can set the verbose level: 13 | 14 | /opt/kazoo/utils/sup/sup whistle_maintenance syslog_level debug 15 |   ( 16 | https://en.wikipedia.org/wiki/Syslog#Severity_levels 17 | ) 18 | /var/log/haproxy.log 19 | 20 | Underestimated tiny log file, really descriptive. It tells you if `haproxy` is doing the needed magic, if not your system won't run nicely. Used by **Kazoo** to access mulitple systems as if they where one (**BigCOUCH** DB): 21 | 22 | /var/log/kamailio/kamailio.log 23 | 24 | 25 | **Kamailio** is your SBC, it receives registration requests  (+some) and validates them: 26 | 27 | /var/log/freeswitch/debug.log 28 | 29 | 30 | **Freeswitch** should be obvious, all calls are handles by it. This file will give you a lot of info. One could also use `fs_cli` on a **Freeswitch** box:  31 |   32 | /var/log/rabbitmq/rabbit.log 33 | 34 | **Rabbitmq** is the communication tool used by **Kazoo** to communicate internally. 35 |   36 | Typical usage: 37 |   38 | User case: 39 | 40 | Inbound call fails: 41 | 42 | 43 | *Just imagine what should happen for a call to be accepted:* 44 | 45 | A call needs to be placed by someone, then delivered to **Kazoo** platform, then accepted by **Freeswitch**, then dealt with. 46 | So.. can you confirm that someone (you?) is dialing the right number? Is the number configured at the DID provider to be routed to **Kazoo**? ARE U SURE?? Ok, so lets first shutdown one FS box or tail `-f /var/log/freeswitch/debug.log` on all FS boxes. 47 | Place a test call...do you see an invite coming in that file? Yes? Great...find the CALL ID and close the log file, then `grep` CALL ID `/var/log/freeswitch/debug.log` The result of that action should be relevant log lines for that call. Check it line for line to see what happened and why it did not do what you expected. 48 | 49 | Most errors in the early stage of the **Kazoo** learning curve have to do with ACLS. Also check Inbound Calls Fail and this page 50 | No invite? That suggests that the call is not delivered to your systems. Can you confirm that someone (you?) is dialing the right number? Is the number configured at the DID provider to be routed to **Kazoo**? ARE YOU SURE? If so, please check if you dont have a firewall in place thats messing stuff up. One easy but dangerous way to test is to disable the firewall for a while. Still nothing? If you are using a SIP address when directing calls to **Kazoo**, please check DNS and DNS propagation, if unsure try to use the ip address instead of domain name. Still nothing you could check the **Kazoo** log, i dont think it will contain anything but you (actually i) never know. 51 |   52 |   53 |   54 |   55 |   56 |   57 |   58 | -------------------------------------------------------------------------------- /doc/kazoo/kazoo-servermisc.md: -------------------------------------------------------------------------------- 1 | ## Install Kazoo UI 2 | 3 | 4 | 5 | How to install **Kazoo** UI in a redundant fashion, server-side. Compressing **Kazoo UI** to load faster. 6 |   7 | -------------------------------------------------------------------------------- /doc/kazoo/kazoo-uimisc.md: -------------------------------------------------------------------------------- 1 | ## Kazoo UI 2 | 3 | 4 | 5 | Contrary to popular belief, **Kazoo UI** doesn't need to be installed anywhere in particular. In fact, since **Kazoo UI** just accesses the native **Kazoo** APIs, you can install **Kazoo UI** right on your local computer with the only requirement being a local web browser. 6 | This opens up interesting opportunities. You can develop **Kazoo UI** add-ons locally without touching your production servers which are running a different version of **Kazoo UI**, all using the same APIs. The **Kazoo** APIs can be on **Kazoo** Hosted or your own installation. This essentially allows you to heavily customize the user experience, whether or not you utilize a hosted API platform managed by you. 7 |   8 | -------------------------------------------------------------------------------- /doc/kazoo/open-source.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | 4 | 5 | The easiest way to get started is to fork one of the projects below on **GitHub**, make your changes, and issue a pull request to us. 6 | But I can't code (or don't know **Erlang**, **Javascript**, or **C**). How can I contribute? Our wiki is always in need of updating, expanding, clarifying, spell-checking, etc. We know the value of this work and how much it means to us when our community helps in ways that helps everyone.We also have a forum that always needs interaction. Helping answer questions, clarify details, and filing tickets for new bugs/features helps our roadmap grow in ways that are actually useful (and not what we might think are useful). 7 | 8 | Translation from English into your native tongue is also of great benefit. So much software ends up designed for use in the United States that modifying it for international use presents bigger hurdles than it should. We've known from day one that we wanted the platform to be useful around the world and have tried to not hard code aspects that are particular to the way the United States does telephony. Any help in making the platform usable in other parts of the world is greatly appreciated. 9 | 10 | Happen to have deep pockets? We are always looking for feature sponsors and are always willing to accept donations as well. T-shirts, funny photos, **2600hz** branded Kazoos and more, can be yours as a small token of our appreciation. Want Karl to sing your favorite song and post it on Youtube? Let us know and we'll figure out the dollar amount (we also accept gold bullion). Have other ideas of how you'd like to help? Let us know! 11 |   12 | 13 | ### Give me code, or give me death! 14 | 15 | ## **Kazoo** Git Repo: 16 | 17 | git clone  18 | https://github.com/2600hz/kazoo.git 19 | 20 | 21 | ## **Kazoo UI** Git Repo: 22 | 23 | git clone  24 | https://github.com/2600hz/kazoo_ui.git 25 | 26 | 27 | ## **FreeSWITCH** - custom build: 28 | 29 | git clone  30 | https://github.com/2600hz/FreeSWITCH.git 31 |   32 |   33 | ## Dependencies 34 | 35 | If you are brave enough to compile from source, please consider installing these dependencies: 36 | 37 | yum install libxslt zip unzip gcc libtool libstdc++-devel 38 | yum install gunzip #might fail, ignore it 39 |   40 | -------------------------------------------------------------------------------- /doc/kazoo/optimize-kazoo-ui.md: -------------------------------------------------------------------------------- 1 | ## Optimize Kazoo UI 2 | 3 | 4 | 5 | How to optimize **Kazoo** UI? The optimizer will reduce the size, obfuscate and reformat all the CSS and JS file in the project.  6 | 7 | 8 | ## Prerequisites 9 | 10 | In order to use the optimizer, you will need the `requirejs` optimizer (https://github.com/jrburke/r.js). You will need to install `node` and `npm`. 11 | 12 | 13 | ## The build file 14 | 15 | The optimizer is based on a build file that will specify what options you will want to use to optimize the project. Here is the one that I used to do it: 16 | 17 | ({baseUrl: ../kazoo_ui/ 18 | ,dir: optimized 19 | ,preserveLicenseComments: false, 20 | 21 | }) 22 | 23 | If you want to get more information about what option is used for what I recommend taking a look at the example here:  24 | https://github.com/jrburke/r.js/blob/master/build/example.build.js 25 |   26 | 27 | ## Execution 28 | 29 | Let's say that you have put the optimizer file (r.js) and your build file (build.js) in a folder at the same level than the **Kazoo UI** folder. The command that you will need to execute is the following one: 30 | 31 | node r.js -o build.js 32 | 33 | When the execution is done, you should have a folder called "optimized" in the same folder as `r.js`. 34 | -------------------------------------------------------------------------------- /doc/kazoo/rating-engine.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Rating Engine 2 | 3 | 4 | 5 | ## Build Your Own Rating Engine 6 | 7 | Here are the steps to easily add your own rating engine to **Kazoo** (and bypass the **HotOrNot WhApp**). 8 | 9 | How calls are rated in **Kazoo** 10 | 11 | When a new call is received in the `ecallmgr` application, a rating request is published onto the AMQP bus. Any application bound for those rate request events will receive the JSON payload and can optionally respond with a rate response. The first valid response received will be the rate applied to the call. 12 | 13 | 14 | ## Rate Request JSON 15 | 16 | **Kazoo** generates a rate request payload published onto the callmgr exchange with a routing key of rate.req. The JSON payload will look something along the lines of: 17 | 18 | **Rate Request JSON:** 19 | 20 | {To-DID:+14158867900 21 | ,Call-ID:abc123def456ghi789 22 | ,Event-Category:rate 23 | ,Event-Name:req 24 | ,Msg-ID:msg_id_9876 25 | ,Server-ID:amqp_queue_name 26 | ,App-Name:sending_app 27 | ,App-Version:sending_app_ver 28 | ,Node:ecallmgr@host.com 29 | ,Account-ID:qwerty1234567890 30 | ,From-DID:+14158867915 31 | ,Options:[] 32 | ,Direction:inbound 33 | 34 | } 35 | 36 | 37 | **Description of the Rate Request JSON payload:** 38 | 39 | ```Field:Description 40 | Value : Required 41 | To-DID: The dialed number 42 | The dialed number off the INVITE 43 | Yes 44 | Call-ID: The unique identifier for this channel 45 | String: Yes 46 | Event-Category: The class of event 47 | Rate: Yes 48 | Event-Name: The name of the event 49 | Req: Yes 50 | Msg-ID: The ID of this rate request message 51 | String: Yes 52 | Server-ID: The name of the AMQP queue of the sender, empty if no response is needed 53 | String: Yes 54 | App-Name: The name of the application that published the request 55 | String: Yes 56 | App-Version: The version of the application that published the request 57 | String: Yes 58 | Node: The **Erlang** VM node name of the sender 59 | String: Yes 60 | Options: Arbitrary list of strings set by sender, usually used by receiving to filter list of applicable rates 61 | List of strings: No 62 | ``` 63 | 64 | ## Direction 65 | 66 | The direction of the call, relative to Kazoo; 67 | outbound: meaning from **Kazoo** to the endpoint and 68 | inbound: meaning from the endpoint to **Kazoo** 69 | inbound/ outbound:No 70 | 71 | `` 72 | Account-ID: The **Kazoo** account ID associated with this cal (if any) 73 | String: No 74 | From-DID : The Caller ID number (if available) 75 | String: No 76 | ``` 77 | 78 | ## Route Response JSON 79 | 80 | Any application which receives rate requests and wishes to respond will need to publish the response to the targeted exchange using the Server-ID from the rate request payload as the routing key. Assuming the request above, a potential response would be constructed thusly: 81 | 82 | **Rate Response JSON:** 83 | 84 | {Rate:0.05 85 | ,Call-ID:abc123def456ghi78 86 | ,Rate-Increment:60 87 | ,Rate-Minimum:60 88 | ,Discount-Percentage:0 89 | ,Surcharge:1.00 90 | ,Rate-Name:expensive_rate 91 | ,Base-Cost:1.05 92 | ,Msg-ID:msg_id_9876 93 | ,Update-Callee-ID:true 94 | ,Event-Category:rate 95 | ,Event-Name:resp 96 | ,App-Name:hotornot 97 | ,App-Version:1.0.0 98 | ,Server-ID: 99 | ,Node:whistle_apps@host.com 100 | 101 | } 102 | 103 | ``` 104 | 105 | **Description of the Rate Response JSON payload:** 106 | 107 | 108 | ```Field:Description 109 | Value: Required 110 | Rate: The cost of the rate 111 | Float: Yes 112 | Call-ID: Identifier of the channel 113 | String:Yes 114 | Event-Category: Class of message 115 | Rate:Yes 116 | Event-Name: Name of the message type 117 | Resp: Yes 118 | App-Name: Name of the responding application 119 | String: Yes 120 | App-Version: Version of the responding application 121 | String: Yes 122 | Msg-ID: The ID from the request payload that identifies the request message 123 | String: Yes 124 | Node: The responder's node (useful mostly for debugging) 125 | String: Yes 126 | Server-ID: Empty, since no response to this message will be sent 127 | Rate-Increment: How many seconds before Rate is applied 128 | Integer (defaults to 60): No 129 | Rate-Minimum: Minimum number of seconds to bill the call for, regardless of call duration 130 | Integer, defaults to 60: No 131 | Discount-Percentage: Apply a discount to the cost of the call 132 | Integer: No 133 | Surcharge: Flat amount to bill the call (in addition to per-minute charges) 134 | Float: No 135 | Rate-Name: Arbitrary name for the rate 136 | String: No 137 | Base-Cost: (Rate * (Rate-Minimum div 60)) + Rate-Surcharge 138 | Float: No 139 | Update-Caller-ID: If `true`, will prepend the Callee ID name with the rate of the call 140 | Boolean, defaults to `false`: No 141 | ``` 142 | 143 | ## Rate Field 144 | 145 | The `rate` fields (Rate, Rate-Increment, etc) will be set on the channel and available in the 146 | Custom-Channel-Vars of the resulting CDR. A simple formula is used to calculate the cost of the call:  147 |   148 |   149 | R = Rate 150 | RI = Rate-Increment 151 | RM = Rate-Minimum 152 | Sur = Surcharge 153 | Secs = Billing Seconds 154 | When RM `60 = Sur + ((RM / 60) * R)` 155 | When `RM = 60 = Sur + ((RM / 60) * R) + ceiling((Secs - RM) / RI) * ((RI / 60) * R)))` 156 |   157 |   158 | -------------------------------------------------------------------------------- /doc/kazoo/service-limits.md: -------------------------------------------------------------------------------- 1 | ## Kazoo Service Limits 2 | 3 | 4 | 5 | **Kazoo** provides facilities to limit what services are being used by a particular client. Limits can involve simultaneous outbound channels, simultaneous inbound channels, total channel count, and enforcement of credit minimums. 6 | 7 | **These features can be used to provide:** 8 | 9 | 1. Two-way SIP Trunks 10 | 2. Inbound SIP Trunks 11 | 3. Per-Minute SIP Trunks 12 | 4. Per-Minute Hosted PBX Calling 13 | 5. General Resource Consumption Limits 14 | 6. Pre-Pay Cost Tracking 15 | 7. Post-Pay Cost Tracking 16 | 8. Basic Fraud Limitations (Prevent post-pay accounts from dipping 17 | too deep into their credit) 18 |   19 | **Talking Points:** 20 | 21 | 1. How to tell if a call was rated as a per-minute or a flat-rate. 22 | 2. How to tell if a call was billed properly or not (accounting table?). 23 | 3. How to double-check billing, if possible. 24 | 4. Tools to run to re-run billing, if possible, for a CDR. 25 |   26 | -------------------------------------------------------------------------------- /doc/kazoo/service-plans.md: -------------------------------------------------------------------------------- 1 | ## Service Plans 2 | 3 | Service Plans define costs associated with various services in the system. Services figure out how many devices, trunks, users and similar 4 | features are in use for an account. Those totals are applied to one or more service plans on the account and then passed to a bookkeeper to handle the actual billing functionality. 5 | 6 | ## Use Case 7 | 8 | New guy just installed his cluster, wants to bill his clients 9 | 10 | ## Configuring a Service Plan 11 | 12 | A service plan document defines services, such as DIDs or SIP devices, which have a cost. It also defines the name of the corresponding element that will be used in your given bookkeeper (i.e. you may call DIDs in Quickbooks Phone Numbers, and therefore you need to map our name to your name). Together, these two concepts make up a service plan. The service plan document(s) is located within a reseller's account. The master account on every system is automatically considered the default reseller. 13 | 14 | ## Master / Default Service Plans 15 | 16 | Standard accounts are accounts which live underneath a reseller. By default, the master account is the first reseller in the system. For the purposes of this example, we'll assume you're creating your first account beneath your master/reseller account. 17 | 18 | 19 | **Find Your Master Account** 20 | 21 | 1. You'll need the account-ID if you don't already know it. 22 | 23 | 2. Go into the master account's database via **BigCouch**'s Futon interface 24 | 25 | 3. Create a new document 26 | 27 | 4. Add the field `pvt_type` with the value `service_plan` 28 | 29 | 5. Add a plan 30 | 31 | ```json 32 | { 33 | "devices": { 34 | "_all": { 35 | "as": "sip_devices", 36 | "cascade": "true", 37 | "name": "SIP Device", 38 | "rates": { 39 | "100": 149.95, 40 | "20": 24.95, 41 | "5": 0, 42 | "50": 49.95 43 | } 44 | } 45 | }, 46 | "limits": { 47 | "inbound_trunks": { 48 | "name": "Inbound Trunk", 49 | "rate": 19.99 50 | }, 51 | "twoway_trunks": { 52 | "name": "Two-Way Trunk", 53 | "rate": 29.99 54 | } 55 | }, 56 | "number_services": { 57 | "cnam": { 58 | "activation_charge": 2, 59 | "name": "CNAM Update" 60 | }, 61 | "e911": { 62 | "discounts": { 63 | "single": { 64 | "rate": 5 65 | } 66 | }, 67 | "name": "E911 Service", 68 | "rate": 5 69 | }, 70 | "port": { 71 | "activation_charge": 5, 72 | "name": "Port Request" 73 | } 74 | }, 75 | "phone_numbers": { 76 | "did_us": { 77 | "cascade": "false", 78 | "discounts": { 79 | "cumulative": { 80 | "maximum": 2, 81 | "rate": 0.5 82 | } 83 | }, 84 | "name": "US DID", 85 | "rate": 1 86 | }, 87 | "tollfree_us": { 88 | "cascade": "true", 89 | "minimum": 10, 90 | "name": "US Tollfree", 91 | "rate": 5 92 | } 93 | } 94 | } 95 | ``` 96 | 97 | **Reseller Service Plans** 98 | 99 | **Service Plan Minimums** 100 | 101 | **List Available Service Plans** 102 | ```bash 103 | curl -v -X GET -H "X-Auth-Token: $AUTH_TOKEN" "http://localhost:8000/v2/accounts/$ACCOUNT_ID/services/available" | jq 104 | ``` 105 | **Retrive currently assigned service plan** 106 | ``` 107 | curl -v -X GET -H "X-Auth-Token: $AUTH_TOKEN" "http://localhost:8000/v2/accounts/$ACCOUNT_ID/services" | jq 108 | ``` 109 | **Service Plan Summary** 110 | ``` 111 | curl -v -X GET -H "X-Auth-Token: $AUTH_TOKEN" "http://localhost:8000/v2/accounts/$ACCOUNT_ID/services/summary" | jq 112 | ``` 113 | **Assigning Service Plan to an Account** 114 | ``` 115 | curl -v -X POST -H "X-Auth-Token: $AUTH_TOKEN" "http://localhost:8000/v2/accounts/$ACCOUNT_ID/services/$PLAN_ID" | jq 116 | ``` 117 | **Remove Service Plan from Account** 118 | ``` 119 | curl -v -X DELETE -H "X-Auth-Token: $AUTH_TOKEN" "http://localhost:8000/v2/accounts/$ACCOUNT_ID/services/$PLAN_ID" | jq 120 | ``` 121 | 122 | **Promoting Account to Reseller Status** 123 | ``` 124 | curl -s -X PUT -H "X-Auth-Token: {AUTH_TOKEN}" http://localhost:8000/v2/accounts/{ACCOUNT_ID}/reseller 125 | ``` 126 | **Demote Account from Reseller Status** 127 | ``` 128 | curl -s -X DELETE -H "X-Auth-Token: {AUTH_TOKEN}" http://localhost:8000/v2/accounts/{ACCOUNT_ID}/reseller 129 | ``` 130 | **Setting the default / global service plan** 131 | 132 | **Checking if someone is properly setup as a reseller?** 133 | 134 | **APIs for any of the above (now or future)** 135 | 136 | **What does default_service_plan do?** 137 | 138 | **How to check that a bookkeeper is loaded/running?** 139 | To check if braintree app is running 140 | ``` 141 | sup kapps_controller list_apps 142 | ``` 143 | Braintree app should be in this list. 144 | 145 | **What happens if a bookkeeper is not working/loaded/running?** 146 | 147 | ## Advanced Features 148 | 149 | It may be useful to have a reseller account with subaccounts, who in turn have their own subaccounts, all billing back to the reseller account. An account's reseller is determined by looking at each parent until the first account with a reseller flag is found. If no such account is found, the master account is used. 150 | 151 |   152 | ## IMMEDIATE ACTION AFTER CHANGE: 153 | 154 | USER ADDS DEVICE 155 | 156 | DEVICES ARE RE-COUNTED 157 | 158 | SERVICES DATABASE IS UPDATED WITH LATEST COUNT 159 | 160 | ACCOUNT IS MARKED AS DIRTY 161 | 162 | ## PERIODIC MAINTENANCE: 163 | 164 | SCAN FOR DIRTY ACCOUNTS 165 | 166 | APPLY SERVICE PLAN TO ACCOUNT SERVICES 167 | 168 | PASS THE LIST OF BILLABLE ITEMS TO THE BOOKKEEPER 169 | 170 | IF CUSTOMER IS PART OF A RESELLER, DIRTY THE RESELLER'S ACCOUNT 171 | 172 | ## BOOKKEEPER: 173 | 174 | TAKE NORMALIZED LIST OF ITEMS 175 | 176 | PASS TO BOOKKEEPER SPECIFIC LOGIC (Could be WHMCS, Braintree, Stripe, etc.) 177 | 178 | RETURNS ACCOUNT STATUS (GOOD STANDING or ERROR) 179 | -------------------------------------------------------------------------------- /doc/kazoo/sip-flowchart.md: -------------------------------------------------------------------------------- 1 | ## Kazoo SIP Flowchart 2 | 3 | 4 | 5 | *These flowcharts are not meant to be exhaustive; merely guides to helping diagnose where a call veered off the tracks and failed.* 6 |   7 | Failed does not mean the call wasn't answered or a busy signal was received; those are successful in that the call was placed properly and an acceptable end of the call was encountered. Failure, in this case, are ends of the SIP dialogue that are unexpected or not supposed to happen. Examples include, but aren't limited to, 404 Not Found or 403 Forbidden. 8 |   9 | For more response codes and more in-depth illustrations of SIP dialogues - Wikipedia is a great resource. 10 | -------------------------------------------------------------------------------- /doc/kazoo/tdm.md: -------------------------------------------------------------------------------- 1 | ## Kazoo and TDM 2 | 3 | 4 | 5 | **Kazoo** allows you to connect your **FreeSWITCH** servers to TDM-based networks via PRIs or other digital circuits. Using cards 6 | supporting the **FreeSWITCH FreeTDM** system you can program **Kazoo** to utilize hardware inside **FreeSWITCH** servers to call via those 7 | circuits. The current **Kazoo FreeTDM** support is fairly limited. You must have a TDM card installed, with similar capacity, in each system where calls are to be placed. To configure the user of TDM circuits, add a document such as the sample below to your offnet or carriers record: 8 | 9 | ``` 10 | {_id: 23c1c9ae35fc7b7318d6128af00009bb, 11 | pvt_type: resource, 12 | name: FreeTDM Pretend Card, 13 | enabled: true, 14 | flags: [ ], 15 | weight_cost: 60, 16 | rules: [^\\+1(\d{10})$], 17 | gateways: [{server: fs-tdm.2600hz.com, 18 | prefix: 1, suffix: , 19 | codecs:[PCMU], progress_timeout: 8, 20 | enabled: false, span: 1, invite_format: e164, 21 | endpoint_type: freetdm, channel_selection: ascending} ] 22 | 23 | } 24 | ``` 25 | 26 | 27 | -------------------------------------------------------------------------------- /doc/mkdocs/index.md: -------------------------------------------------------------------------------- 1 | Everything you need to know to run and manage 2600hz software. 2 | 3 | -------------------------------------------------------------------------------- /doc/monster/login-issues.md: -------------------------------------------------------------------------------- 1 | ## Login Issues 2 | 3 | 4 | 5 | 1. User can't login: Check that the API URL set up in the JS / `config.js` is correct. 6 | 7 | 2. User can login but lands on a white page: Check that the API URL of the apps are setup correctly in the database. Apps are defined in the admin account database, in the `app_store` view. If you need to update these documents, refresh the views: 8 | 9 | `sup kapps_maintenance blocking_refresh` 10 | 11 | 12 | ## Phone Numbers Issues 13 | 14 | 1. User can't search/buy/list numbers: Make sure that the correct `phonebook_url` are defined properly in the `system_configs`, `crossbar.phone_numbers` and `number_manager.other`. 15 | -------------------------------------------------------------------------------- /doc/monster/optimize-monster.md: -------------------------------------------------------------------------------- 1 | ## Optimize Kazoo UI 2 | 3 | 4 | 5 | How to optimize **Kazoo UI**? 6 | 7 | 8 | The optimizer will reduce the size, obfuscate and reformat all the CSS and JS file in the project  9 | 10 | 11 | ## Prerequisites 12 | 13 | In order to use the optimizer, you will need... the `requirejs` optimizer (https://github.com/jrburke/r.js). 14 | You will need to install `node` and `npm`. 15 | 16 | 17 | ## The Build file 18 | 19 | The optimizer is based on a build file that will specify what options you will want to use to optimize the project. 20 | Here is the one that I used to do it: 21 | 22 | 23 | { 24 | 25 | baseUrl: monsterToOptimized/ 26 | ,mainConfigFile: monsterToOptimized/js/main.js 27 | ,dir: optimized 28 | ,findNestedDependencies: true, 29 | ,preserveLicenseComments: false 30 | ,removeCombined: true 31 | ,modules: [{ name: js/main 32 | ,exclude: [config]}, 33 | 34 | 35 | { 36 | 37 | name: apps/common/app 38 | ,exclude: [bootstrap-switch 39 | ,toastr 40 | ,jquery 41 | ,monster 42 | ,timepicker 43 | ,underscore 44 | ,ddslick 45 | ]}, 46 | 47 | 48 | { 49 | 50 | name: apps/voip/app 51 | ,exclude: [ jstz 52 | ,monster-timezone 53 | ,toastr 54 | ,jquery 55 | ,mask 56 | ,monster 57 | ,timepicker 58 | ,underscore 59 | ,chosen 60 | ,chart 61 | ]}, 62 | 63 | 64 | { 65 | 66 | name: apps/callflows/app 67 | ,exclude: [ 68 | ,jquery 69 | ,monster 70 | ,underscore 71 | ,chosen 72 | ,mask 73 | ,monster-timezone 74 | ,slider 75 | 76 | ]} 77 | 78 | ]} 79 | 80 | If you want to get more information about what option is used for what I recommend taking a look at the example here:  81 | 82 | https://github.com/jrburke/r.js/blob/master/build/example.build.js 83 | 84 | 85 | ## Execution 86 | 87 | Let's say that you have put the optimizer file `r.js` and your build file `build.js` in a folder at the same level than the **Kazoo UI** folder. The command that you will need to execute is the following one: 88 | 89 | node r.js -o build.js 90 | 91 | When the execution is done, you should have a folder called 'optimized' in the same folder as `r.js` (Note: On Windows, you might have to type `r.js.cmd` instead of `r.js`) 92 | -------------------------------------------------------------------------------- /doc/operation/cluster-health.md: -------------------------------------------------------------------------------- 1 | ## Cluster Health 2 | 3 | 4 | 5 | These instructions assume you are running as `root`. All services MUST be working as indicated in order to have a properly running **Kazoo** platform. 6 | 7 | 8 | ## FreeSWITCH 9 | 10 | Is **FreeSWITCH** running? 11 | ```  12 | service freeswitch status 13 | 14 | freeswitch(pid:xxxx): up 15 | ``` 16 | If not then: 17 | 18 | `service freeswitch start` 19 |   20 | Is **FreeSWITCH** Connected? 21 | 22 | **FreeSWITCH** must be connected to the rest of the platform. You can check this from within **FreeSWITCH** itself. Enter the 23 | 24 | **FreeSWITCH** CLI and check for erlang listener(s). You should have at least one: 25 | ``` 26 | cli -x 27 | 28 | erlang listeners 29 | ``` 30 | You should see at least one of your other servers listed. If you don't, or there are no listeners, then **FreeSWITCH** is running by `ecallmgr` is not connected to it. 31 |   32 | 33 | ## ECallMgr 34 | 35 | Is **ECallMgr** Running? 36 | 37 | **ECallMgr** connects the Kazoo platform with all **FreeSWITCH** servers. It is the primary link between **FreeSWITCH** and the rest of the platform. 38 |   39 | `service ecallmgr status 40 | 41 | [freeswitch@fs001.yourserver.com, 42 | 43 | freeswitch@fs002.yourserver.com]` 44 | 45 | This command should return a list of **FreeSWITCH** nodes which **ECallMgr** is connected to, in JSON format. 46 |   47 | If it does not, try restarting `ECallMgr`: 48 | 49 | `service ecallmgr restart` 50 | 51 | Check **RabbitMQ** status 52 | 53 | **RabbitMQ** provides the glue between **FreeSWITCH** and the **Kazoo** Applications: 54 | ` 55 | service rabbitmq-server status` 56 | 57 | That command should return something like: 58 |  ``` 59 | #Status of node rabbit@mydomain.com... 60 | 61 | [{pid,2049 62 | }, 63 |   64 | { running_applications,[{rabbit, 65 | 66 | RabbitMQ, 2.7.0 }, 67 |                          68 | {os_mon, CPO  CXC 138 46, 2.2.6}, 69 |                          70 | {sasl, SASL  CXC 138 11, 2.1.9.4}, 71 |                          72 | {mnesia, MNESIA  CXC 138 12, 4.4.19}, 73 |                          74 | {stdlib,ERTS  CXC 138 10, 1.17.4}, 75 |                          76 | {kernel,ERTS  CXC 138 10,2.14.4}]}, 77 |   78 | {os,{unix,linux}}, 79 |   80 | {erlang_version, 81 | 82 | Erlang R14B03 (erts-5.8.4) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:30] [kernel-poll:true]\n}, 83 |   84 | {memory,[{total, 26165880}, 85 |            86 | {processes, 10842712}, 87 |            88 | {processes_used, 10828488}, 89 |            90 | {system, 15323168}, 91 |            92 | {atom, 1122017}, 93 |            94 | {atom_used, 1116886}, 95 |            96 | {binary, 115952}, 97 |            98 | {code, 11271185}, 99 |            100 | {ets, 865008}]}, 101 |   102 | {vm_memory_high_watermark,0.3999999997516473}, 103 |   104 | {vm_memory_limit, 322122547}]` 105 | 106 | ...done. 107 |   108 | #If not then: 109 |   110 | `# service rabbitmq-server restart` 111 |  ``` 112 | 113 | ## Kazoo Applications 114 |   115 | `# service whapps status` 116 |   117 | Should give you something like: Searching for running **WhApps** on `whistle_apps@mydomain.com` 118 | ``` 119 | [cdr,sysconf,conference,registrar,hangups,media_mgr,crossbar,callflow,stepswitch] 120 |  ``` 121 | If it does not, try restarting it. 122 | 123 | `service whapps restart` 124 |   125 | Some of these are optional and some are mandatory. I believe **Crossbar** is necessary for **Winkstart** to work. If **Crossbar** is not there you can try running 126 | 127 | `whapps_maintenance:refresh().` 128 | 129 | from the 'Some Useful Commands' section of this wiki. 130 | 131 | 132 | ## HAProxy 133 |   134 | `# service haproxy status` 135 | 136 | haproxy (pid  xxxx) is running...and if not 137 |   138 | `# service haproxy restart` 139 |   140 | 141 | ## Check BigCouch 142 | 143 | Assuming localhost 144 |   145 | `# curl localhost: 5984 146 | 147 | { couchdb: Welcome, version: 1.1.1, bigcouch: 0.4.0}` 148 |   149 | if not then check and double check your haproxy config and status and try: 150 |   151 | `# service bigcouch restart` 152 |   153 |   154 | -------------------------------------------------------------------------------- /doc/operation/ets-persist.md: -------------------------------------------------------------------------------- 1 | ## ETS Persistence 2 | 3 | 4 | 5 | ## Don't lose your data 6 | 7 | When using ETS tables, when the owner dies, typically the ETS table will be deleted as well. On some occasions, this is not desired behaviour. See Steve Vinoski's blog article for more. 8 | 9 | 10 | ## Kazoo's Approach 11 | 12 | We have an implementation of an ETS manager server process that will handle creating the ETS table, handing off control to the process that will actually use the table for work, receiving notifications when that process dies, and handing off control once a replacement process is available. 13 | 14 | 15 | ## How it works 16 | 17 | If you navigate over to `kazoo_etsmgr_srv.erl`, you will find the `gen_server` code to manage an ETS table. To make use of the server, here's the typical steps involved: 18 | 19 | Define the ETS properties 20 | 21 | Define the ETS table's name 22 | 23 | Define the `find_me_function`, a function that returns either the `PID` of the new process to pass control to, or `undefined` if no process is ready to assume control yet. 24 | 25 | Define the table_options; see `ets:new/2`for those. 26 | 27 | Define the `gift_data`; typically this is ignored. 28 | 29 | Start the manager process in the supervisor 30 | 31 | Handle gaining control of the ETS table from the manager process 32 | 33 | **How it might look** 34 | 35 | This might go in your `gen_server`, `gen_listener`, etc. 36 | 37 | 1. Defining the ETS properties 38 | ``` 39 | -export([table_id/0 40 | ,table_options/0 41 | ,find_me_function/0 42 | ,gift_data/0 43 | ]). 44 |   45 | table_id() - 46 | ?MODULE. %% Any atom will do 47 | table_options() -[]. 48 | find_me_function() - 49 | whereis(?MODULE). 50 | gift_data() - 51 | ok. 52 | #This might go in your application's supervisor 53 | 54 | #Start the manager process 55 | 56 | -define(CHILDREN, [?WORKER_ARGS(kazoo_etsmgr_srv, [[{ 57 | table_id, your_srv:table_id()} 58 | ,{ 59 | 60 | table_options, your_srv:table_options()} 61 | ,{find_me_function, fun your_srv:find_me_function/0}, 62 | { 63 | gift_data 64 | ,your_srv:gift_data()}]]) 65 | ,... 66 | 67 | ]). 68 | 69 | ``` 70 | In your `gen_server`/`gen_listener`, handle receiving control of the ETS table 71 | 72 | **Gain control** 73 | 74 | `handle_info({ 75 | ETS-TRANSFER 76 | ,TableId, From, GiftData}, State) - 77 | #Now this process can write to the ETS table 78 | {noreply, State};` 79 |   80 | Now your process should be all set to write to the ETS table (and you can do the reads in the calling processes to parallelize reading, unless you want to serialize the reads through the server as well). 81 | -------------------------------------------------------------------------------- /doc/operation/login-noapps.md: -------------------------------------------------------------------------------- 1 | ## Login Kazoo UI 2 | 3 | 4 | 5 | If you login to your **Kazoo** UI but no applications appear at the top of the page, your user or your config.js have mismatched API URLs. For security and validation, when you login the system checks to make sure the API you are logging into matches the API you are requesting apps from. If they don't, things won't work right (without special CORS headers - that's an advanced topic). 6 | 7 | To fix this issue, follow these steps: 8 | 9 | 10 | ## Step 1: Identify the Authentication URL 11 | 12 | In your Kazoo UI's HTML folders there is a folder named config and a file named config.js. Inside that file is a configuration section which includes a default auth app which specifies the API you are authenticating against. 13 | 14 | winkstart.apps = { 15 | auth: { api_url: http://YOUR.SERVER.NAME.COM:8000/v1 } 16 | }; 17 | 18 | Note the `api_url` that is set. 19 | 20 | 21 | ## Step 2: Identify the User's Apps 22 | 23 | For each user that logs in they have a configured list of apps. These apps also utilize an API URL. If you are running **Kazoo UI** on your own server you'll want users to be utilizing the same API they authenticated with. 24 | To find the user's app list, take the following steps: 25 | 26 | Login to Futon (your **CouchDB**'s graphical user interface) on port 5984 on your server.    27 | 28 | http://YOUR.SERVER.NAME.COM:5984/_utills/ 29 | 30 | Click on the correct account ID to enter your account database. Accounts are formatted `account/22/33/4567890456789045678904567890` 31 | Once inside the account database, on the top right corner of the screen is a drop-down with all the available views. Click on the drop-down and select `users/ crossbar` 32 | 33 | Verify that the API URL for your App matches the API URL you logged in with (in Step 1)  34 |   35 | If you make a change, make sure you logout and log back in as the list of Apps is cached on your browser. 36 |   37 | You can add the complete list of apps manually, like this: 38 |   39 | { 40 | voip: { 41 | label: Hosted PBX, 42 | icon: phone, 43 | api_url: http://YOUR.SERVER.NAME.COM:8000/v1, 44 | 45 | ui_flags: { 46 | provision_admin: true, 47 | super_duper_admin: true 48 | }, 49 | 50 | default: false 51 | }, 52 | 53 | pbxs: { 54 | label: PBX Connector, 55 | icon: pbx, 56 | api_url: http://YOUR.SERVER.NAME.COM:8000/v1, 57 | default: false 58 | }, 59 | 60 | numbers: { 61 | label: Number Manager, 62 | icon: menu1, 63 | api_url: http://YOUR.SERVER.NAME.COM:8000/v1, 64 | default: false 65 | }, 66 | 67 | accounts: { 68 | label: Accounts, 69 | icon: account, 70 | api_url: http://YOUR.SERVER.NAME.COM:8000/v1, 71 | default: true 72 | }, 73 | 74 | developer: { 75 | id: developer, 76 | label: Developer, 77 | icon: connectivity, 78 | desc: API Developer Tool, 79 | api_url: http://YOUR.SERVER.NAME.COM:8000/v1, 80 | default: false 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /doc/operation/monitoring.md: -------------------------------------------------------------------------------- 1 | ## Monitoring Overview 2 | -------------------------------------------------------------------------------- /doc/operation/shared_line_appearance.md: -------------------------------------------------------------------------------- 1 | # Shared Line Appearance (SLA) 2 | 3 | Legacy PBXes often had a feature called **Shared Line Appearance** for incoming calls. The functionality basically allowed multiple phones to have a line key tied to an extension or DID and offered several features: 4 | 5 | - Incoming calls would ring all configured phones simultaneously 6 | - When a phone answered the call, the status light would turn green; on the other phones, the status light would turn red 7 | - If the caller is placed on hold, all phones' (answering or not) status lights will blink red 8 | - Any phone with SLA can pickup the on-hold call 9 | - When the call completes, all status lights turn off. 10 | 11 | ## Simulating SLA 12 | 13 | Simulating most of SLA on a Kazoo system is possible. Let's take a look at how this might be achieved. 14 | 15 | ### Incoming calls ring all devices 16 | 17 | This is pretty straight-forward. In Kazoo, one of the callflow actions is a [ring group](https://docs.2600hz.com/dev/applications/callflow/doc/ring_group/). Put any endpoints ([`devices`](https://docs.2600hz.com/dev/applications/crossbar/doc/devices/), [`users`](https://docs.2600hz.com/dev/applications/crossbar/doc/users/), or [`groups`](https://docs.2600hz.com/dev/applications/crossbar/doc/groups/)) into the ring group and all calls to the callflow will ring the endpoints according to the strategy provided. 18 | 19 | ### Status lights 20 | 21 | Kazoo allow updating a pre-defined `presence_id` to with BLF updates. You can set this `presence_id` on the `device` or `user` documents, or you can use the `manual_presence` callflow action to toggle this. Configure the phones' BLF light to subscribe for this `presence_id` value to have it update during calls. 22 | 23 | #### Placing on hold for others 24 | 25 | The closest way to put a call on hold so others could pick it up is using [`call parking`](https://docs.2600hz.com/dev/applications/callflow/doc/park/). Rather than using the hold button on the phone, the answering device can transfer the caller to a parking slot. Once parked, any device that knows the slot number can pick up the call. 26 | 27 | ## Example SLA simulation 28 | 29 | Consider the typical executive with an administrative assistant. The assistant's phone has a line key that will ring when the executive is called. The easiest way to do this is create two devices in Kazoo and assign them to the executive's Kazoo user. 30 | 31 | ### Create the assistant 32 | 33 | 1. Create the assitant's device 34 | ```shell 35 | curl -X PUT \ 36 | -H "X-Auth-Token: $AUTH_TOKEN" \ 37 | -d '{"data":{"name":"Assistant Device", "sip":{"username":"assistant", "password":"to_the_stars"}}}' \ 38 | http://{SERVER}:8000/v2/accounts/$ACCOUNT_ID/devices 39 | ``` 40 | ```json 41 | { 42 | "data":{ 43 | "id":"{ASSISTANT_DEVICE_ID}" 44 | ,... 45 | } 46 | ,... 47 | } 48 | ``` 49 | 50 | ### Create the executive 51 | 52 | 1. [Create a user](https://docs.2600hz.com/dev/applications/crossbar/doc/users/) for the executive. Be sure to include `presence_id="EXT"` in the user object - this is what BLF lights will be tied to when setting up presence. 53 | ```shell 54 | curl -X PUT \ 55 | -H "X-Auth-Token: {AUTH_TOKEN}" \ 56 | -d '{"data":{"first_name":"Exec", "last_name":"Utive", "presence_id":"1000"}}' 57 | http://{SERVER}:8000/v2/accounts/{ACCOUNT_ID}/users 58 | ``` 59 | ```json 60 | { 61 | "data":{ 62 | "id":"{EXECUTIVE_USER_ID}" 63 | ,... 64 | } 65 | ,... 66 | } 67 | ``` 68 | 2. [Create a device](https://docs.2600hz.com/dev/applications/crossbar/doc/devices/) for the executive. 69 | ```shell 70 | curl -X PUT \ 71 | -H "X-Auth-Token: $AUTH_TOKEN" \ 72 | -d '{"data":{"name":"Executive Device", "owner_id":"{EXECUTIVE_USER_ID}", "sip":{"username":"executive", "password":"high_roller"}}}' \ 73 | http://{SERVER}:8000/v2/accounts/$ACCOUNT_ID/devices 74 | ``` 75 | ```json 76 | { 77 | "data":{ 78 | "id":"{EXECUTIVE_DEVICE_ID}" 79 | ,... 80 | } 81 | ,... 82 | } 83 | ``` 84 | 3. [Create the callflow](https://docs.2600hz.com/dev/applications/crossbar/doc/callflows/) using the [ring group](https://docs.2600hz.com/dev/applications/callflow/doc/ring_group/) action to ring both the executive's and assistant's phones. This will also cause a BLF update to `presence_id`@`account.realm` to update the assistant's BLF key. 85 | ```shell 86 | curl -X PUT \ 87 | -H "X-Auth-Token: $AUTH_TOKEN" \ 88 | -d '{"data":{"numbers":["1000"], "flow":{"module":"ring_group","data":{"endpoints":[{"id":"{EXECUTIVE_USER_ID}", "endpoint_type":"user"}, {"id":"{ASSISTANT_DEVICE_ID}", "endpoint_type":"device"}]}}}}' \ 89 | http://{SERVER}:8000/v2/accounts/$ACCOUNT_ID/callflows 90 | ``` 91 | ```json 92 | { 93 | "data":{ 94 | "id":"{EXECUTIVE_CALLFLOW_ID}" 95 | ,... 96 | } 97 | } 98 | ``` 99 | 100 | Now calls to extension 1000 should ring both the executive and the assistant devices. 101 | 102 | ### Create the call park/pickup callflow 103 | 104 | Create a callflow, `*4{presence_id}`, that will have the [`park`](https://docs.2600hz.com/dev/applications/callflow/doc/park/) callflow action. This will be used by the BLF key to simulate the hold/pickup of SLA. You should set the `action` to `auto` and the `slot` to `presence_id`. 105 | ```shell 106 | curl -X PUT \ 107 | -H "X-Auth-Token: $AUTH_TOKEN" \ 108 | -d '{"data":{"patterns":["*4(\\d+)"], "flow":{"module":"park","data":{"action":"auto"}}}}' \ 109 | http://{SERVER}:8000/v2/accounts/$ACCOUNT_ID/callflows 110 | ``` 111 | ```json 112 | { 113 | "data":{ 114 | "id":"{PARK_CALLFLOW_ID}" 115 | ,... 116 | } 117 | } 118 | ``` 119 | 120 | `auto` will retrieve a call if one is parked or park the call if the slot is empty. 121 | 122 | ### Create the BLF key 123 | 124 | Create a BLF key configuration on the assistant's phone to: 125 | 126 | a. The "value" (or equivalent) will be `presence_id` 127 | b. The "extension" (or equivalent) will be `*4{presence_id}` 128 | 129 | This will light up with the corresponding call. 130 | 131 | When the assistant puts the call on hold, the BLF can be used to pickup the call. 132 | -------------------------------------------------------------------------------- /doc/provisioner/README.md: -------------------------------------------------------------------------------- 1 | # Provisioner by 2600Hz -------------------------------------------------------------------------------- /doc/security/iptables.md: -------------------------------------------------------------------------------- 1 | ## Setting up IPTABLES 2 | 3 | 4 | 5 | For your **Kazoo** / **Kamailio** servers create a file called `secure.sh`. Paste the below script into `secure.sh` and modify to fit your environment. 6 | 7 | After saving the file you need to make it executable. 8 | `chmod +x secure.sh` 9 | 10 | Now let's run our new script. 11 | `./secure.sh` 12 | 13 | 14 | ## IPTABLES - Kazoo Server 15 | ``` 16 | #!/bin/bash 17 | # cwd=/etc/sysconfig/ 18 | # 19 | # flush all existing rules and chains 20 | iptables -F 21 | iptables -X 22 | # allow inbound ssh connection on external/public interface 23 | iptables -A INPUT -p tcp --dport 22 --sport 1024:65535 -m state --state NEW,ESTABLISHED -j ACCEPT 24 | # set default policies (allow any outbound, block inbound, restrict forwarding between interfaces) 25 | iptables -P INPUT DROP 26 | iptables -P FORWARD DROP 27 | iptables -P OUTPUT ACCEPT 28 | # Allow TCP 80 and 443 for Winkstart if you installed KAZOO-UI 29 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT 30 | iptables -A INPUT -p tcp --dport 443 -j ACCEPT 31 | # Allow RTP traffic 32 | iptables -A INPUT -p udp --dport 16384:32768 -j ACCEPT 33 | # Allow KAMAILIO traffic 34 | iptables -A INPUT -p tcp --dport 5060 -j ACCEPT 35 | iptables -A INPUT -p tcp --dport 7000 -j ACCEPT 36 | iptables -A INPUT -p udp --dport 5060 -j ACCEPT 37 | iptables -A INPUT -p udp --dport 7000 -j ACCEPT 38 | # allow all inbound traffic coming in on loopback and the internal/private interfaces 39 | #iptables -A INPUT -i eth0 -j ACCEPT 40 | iptables -A INPUT -i lo -j ACCEPT 41 | # allow your home / office ip 42 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # YOUR STATIC IP 43 | #iptables -A INPUT -s "" -j ACCEPT # whatever IP u want if u want someone else to see the db 44 | #PUBLIC IP ADDRESSES OF YOUR SERVERS - REPLACE X'S 45 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - FREESWITCH LOCATION-1 SERVER-1 46 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - FREESWITCH LOCATION-2 SERVER-1 47 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-1 SERVER 001 48 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-1 SERVER 002 49 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-2 SERVER 001 50 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-2 SERVER 002 51 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - WHAPPS/KAZOO LOCATION-1 SERVER-1 52 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - WHAPPS/KAZOO LOCATION-2 SERVER-1 53 | #INTERNET / PRIVATE NETWORK IP ADDRESSES OF YOUR SERVERS 54 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - FREESWITCH LOCATION-1 SERVER-1 55 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - FREESWITCH LOCATION-2 SERVER-1 56 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-1 SERVER 001 57 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-1 SERVER 002 58 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-2 SERVER 001 59 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - BIGCOUCH-DB LOCATION-2 SERVER 002 60 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - WHAPPS/KAZOO LOCATION-1 SERVER-1 61 | iptables -A INPUT -s "XXX.XXX.XXX.XXX" -j ACCEPT # - WHAPPS/KAZOO LOCATION-2 SERVER-1 62 | # block traffic coming into db unless ACCEPTED in INPUT above which 8server setup IP's are placed in with home/office IP's above. 63 | iptables -A INPUT -p tcp --dport 15984 -j DROP 64 | iptables -A INPUT -p tcp --dport 15986 -j DROP 65 | # Allow TCP 8000 and 8443 for Crossbar 66 | iptables -A INPUT -p tcp --dport 8000 -j ACCEPT 67 | iptables -A INPUT -p tcp --dport 8443 -j ACCEPT 68 | # allow inbound traffic for established connections on external/public interface 69 | iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 70 | # block packets coming in with invalid source ips 71 | iptables -A INPUT -s 10.0.0.0/8 -j DROP 72 | iptables -A INPUT -s 172.16.0.0/12 -j DROP 73 | iptables -A INPUT -s 192.168.0.0/16 -j DROP 74 | iptables -A INPUT -s 224.0.0.0/4 -j DROP 75 | iptables -A INPUT -s 240.0.0.0/5 -j DROP 76 | iptables -A INPUT -s 0.0.0.0/8 -j DROP 77 | iptables -A INPUT -s 169.254.0.0/16 -j DROP 78 | iptables -A INPUT -s 127.0.0.0/8 -j DROP 79 | # ICMP 80 | iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/sec -j ACCEPT 81 | # SYN flood limiter 82 | iptables -A INPUT -p tcp --syn -m limit --limit 5/s -j ACCEPT 83 | # catch-all for end of rules 84 | # LOG and DENY other traffic to help identify additional filters required 85 | iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level debug 86 | iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited 87 | # Save settings 88 | # 89 | # On CentOS / Fedora this will output the current IPTables to the stdout, which we can pipe to a file that sets these on boot 90 | iptables-save > /etc/sysconfig/iptables 91 | # 92 | # List rules 93 | # 94 | iptables -L -v 95 | 96 | ``` 97 | -------------------------------------------------------------------------------- /doc/whapp/build.md: -------------------------------------------------------------------------------- 1 | ## Build Your Own WhApp 2 | 3 | 4 | 5 | ## WhApps 6 | 7 | **WhApps** are built to be self-contained units of functionality, varying in scope from a registrar that listens and responds to authentication requests and lookups from **Kazoo**, to a full-blown call center system handling agents, queues, etc. While they do not depend on each other for functionality, some are quite fundamental to the operation of normal telephony services, and therefore we enable them by default. Of course, you are free to write other **WhApps** to replace the functionality provided. 8 | 9 | 10 | ## The WhApp Container 11 | 12 | All code is referenced from: ` $KAZOO/whistle_apps/ directory` 13 | 14 | Starting the container is as simple as `running ./start.sh` from the applications directory. With no additional arguments, the start script will start an **Erlang** Virtual Machine named `whistle_apps`. You can start multiple VMs by passing unique values as an argument to the script (running more than one VM on a single physical server could help increase throughput, depending on the scenario and circumstances). 15 | 16 | Once started, a control script is available to interact with the container in a rudimentary way. `whistle_apps_ctl` has a couple options for managing what **WhApps** are running on the VM: `start_app`, `stop_app`, and `running_apps`. 17 | 18 | `start_app`: takes one parameter, the name of the **WhApp** to start, if its not already started. 19 | 20 | `stop_app`: takes one parameter, the name of the **WhApp** to stop, if it has been started. 21 | 22 | `running_apps`: takes no parameters, returns a list of known running **WhApps**. 23 | 24 | 25 | ## CouchDB 26 | 27 | We use an abstraction layer on top of CouchDB that can be configured to connect to a CouchDB instance (or BigCouch or an http proxy for either). This configuration file can be found at:` $KAZOO/lib/whistle_couch-X.Y.Z/priv/startup.config`. The default config file should look like: 28 | 29 | `{default_couch_host,"localhost",5984,"username","password"}.` 30 | 31 | You may also have an entry similar to the above, but with `{couch_host...}` starting the config line. This is the line added by our **CouchDB** layer when successfully connecting to a **CouchDB** server. If you do not use authentication (Admin party!), leave username and password as empty strings (""). Alternatively, you can set the properties (except port) via the `whistle_apps_ctl` script mentioned above: 32 | 33 | `set_couch_host`: takes one parameter, the hostname to connect to. 34 | 35 | You will be prompted for the username and password. If you haven't set one, press for both questions. Your **WhApps** container should now be reading and writing to the new host. 36 | 37 | 38 | ## RabbitMQ 39 | 40 | Similar to **CouchDB**, we have an abstraction layer for **RabbitMQ** and a simple configuration file for it as well, located at: 41 | 42 | `$ KAZOO/lib/whistle_amqp-X.Y.Z/priv/startup.config` 43 | 44 | It defaults to: `{default_host, "localhost"}`. 45 | 46 | Alternatively, you can set the host via the `whistle_apps_ctl` script: 47 | 48 | `set_amqp_host`: takes one parameter, the host of a **RabbitMQ** broker. 49 | -------------------------------------------------------------------------------- /doc/whapp/jonny5.md: -------------------------------------------------------------------------------- 1 | ## Jonny5 Account Limits 2 | 3 | 4 | 5 | `twoway_trunks`: 0, two way flat rate trunks can only be used if `system_config.jonny5.` `flat_rate_whitelist` `regex` matches AND `system_config.jonny5.` `flat_rate_blacklist` does not. This can also be specified as a `pvt_twoway_trunks`, the lower will be used. 6 | 7 | `inbound_trunks`: 0, exactly the same as two-way trunks but only used for inbound calls. If there are more inbound calls then trunks two-way trunks will be checked next. 8 | 9 | `resource_consuming_calls` : -1, hard limit on the number of calls that can consume one or more resources (IE: inbound to outbound forwarded number is one resource consuming call), can also be specified as `pvt_resource_consuming_calls`, the smallest of the two is used. 10 | 11 | `calls` : -1, total number of calls (resource consuming OR NOT) that an account can have, can also be specified as `pvt_calls`, the smallest of the two is used. 12 | 13 | `allow_prepay` : true, allows the customer to disable per_minute authorizations relying solely on trunks and/or allotments, this will be overridden by `pvt_allow prepay`. 14 | 15 | `pvt_type`: "limits", 16 | 17 | `pvt_vsn`: "1", 18 | 19 | `pvt_account_id`: "d75312345678901234567890", 20 | 21 | `pvt_account_db`: "account%2Fd7%2F53%12345678901234567890", 22 | 23 | `pvt_created`: 63518278682, 24 | 25 | `pvt_modified`: 63518278682, 26 | 27 | `pvt_soft_limit_outbound`: false, allows otherwise unauthorized outbound calls to continue 28 | 29 | `pvt_soft_limit_inbound`: true, allows otherwise unauthorized inbound calls to conitnue 30 | 31 | `pvt_allow_prepay`: true, 32 | 33 | `pvt_allow_postpay`: true, whether or not to allow the account to go negative 34 | 35 | `pvt_max_postpay_amount`: 100, the maximum negative amount that is acceptable 36 | 37 | `pvt_allotments`: 38 | 39 | `did_us`: then classification of a number as per system_config.number_manager.classifiers 40 | 41 | `amount`: 120, the allotment in seconds 42 | 43 | `cycle`: "hourly" the allotment reset period: yearly, monthly, weekly, daily, hourly, minutely 44 | 45 | `pvt_discounts`: 46 | 47 | `did_us`: then classification of a number as per system_config.number_manager.classifiers 48 | 49 | `percentage`: 10 - a percentage discount off the rate of EVERY per_minute call 50 | 51 | `pvt_enabled`: true, should limits be enforced for this account, IF FALSE THE ACCOUNT IS UNRESTRICTED 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/whapp/stepswitch.md: -------------------------------------------------------------------------------- 1 | ## SUP Commands 2 | 3 | 4 | 5 | `sup stepswitch_maintenance flush` 6 | 7 | **Stepswitch** caches the properties of a number when it looks them up, including the account it belongs to. If the number is updated via the API it will be removed from the cache automatically. However, if a manual change is made you may want to flush the cache so it takes affect. 8 | ``` 9 | shell 10 | [root@apps001 example]# /opt/kazoo/utils/sup/sup stepswitch_maintenance flush 11 | sup stepswitch_maintenance refresh 12 | ``` 13 | 14 | **Stepswitch** uses the offnet database which must be set up properly so resources can be queried (see CouchDB views). 15 | This command will ensure the database is present, properly configured to the version of **Stepswitch** and clean up any depreciated documents that might be present. 16 | ``` 17 | shell 18 | [root@apps001 example]# /opt/kazoo/utils/sup/sup stepswitch_maintenance refresh 19 | sup stepswitch_maintenance reload_resources 20 | ``` 21 | 22 | **Stepswitch** monitors the offnet database for any changes (manual or API) and update the in-memory resource list. 23 | However, for older versions without this feature this command must be manually run on all servers with **Stepswitch** before new or updated resources are used. 24 | ``` 25 | shell 26 | [root@apps001 example]# /opt/kazoo/utils/sup/sup stepswitch_maintenance reload_resources 27 | sup stepswitch_maintenance lookup_number {NUMBER} 28 | ``` 29 | 30 | When provided with a number **Stepswitch** will return the known parameters of that number. 31 | These are drawn from the local cache if present or looked up and cached if not. 32 | This provides insight into what account a number is associated with for inbound calls as well as if outbound calls to this number will stay on-net. 33 | 34 | ``` 35 | shell 36 | [root@apps001 example]# /opt/kazoo/utils/sup/sup stepswitch_maintenance lookup_number 4158867900 37 | {ok, 38 | 43579fc0a3aa11e29e960800200c9a66 39 | ,[{force_outbound,false},{pending_port,false},{local,false},{inbound_cnam,false}]} 40 | ``` 41 | 42 | The return is formated as: `{ok, ACCOUNT ID, LIST OF PARAMETERS}` 43 | 44 | ## Parameters: 45 | 46 | `force_outbound` - If this is false when an account in the system calls this number it will no leave the **Kazoo** platform (known as on-net calls) 47 | 48 | `pending_port` - If this is true then the number was created as a port request but no inbound request from the carrier has not occurred yet (port is not complete). 49 | 50 | `inbound_cnam` - If this is true when inbound calls are made to this number a CNAM lookup will take place to update the caller id name in the US. 51 | 52 | ```shell 53 | sup stepswitch_maintenance reload_resources NUMBER 54 | ``` 55 | 56 | This will return a ordered list of resource that would be attempted if the provided number was dialed. 57 | 58 | ```shell 59 | [root@apps001-aa-ord example]# /opt/kazoo/utils/sup/sup stepswitch_maintenance process_number 4158867998 60 | [{ 61 | bae2f840a3d311e29e960800200c9a66 62 | ,0, 63 | sip:+14158867998@192.168.4.15 64 | },{ 65 | bae2f840a3d311e29e960800200c9a66 66 | ,2, 67 | sip:+14158867998@192.168.4.16 68 | }] 69 | ``` 70 | 71 | The return is formatted as a list of: `{RESOURCE ID, DELAY (in seconds), SIP URI}` 72 | -------------------------------------------------------------------------------- /doc/whitelabeling/voicemail_to_email.md: -------------------------------------------------------------------------------- 1 | ## Voicemail to Email 2 | 3 | 4 | 5 | You can fetch the macros available for customization: 6 | 7 | ```shell 8 | curl -H "X-Auth-Token: $AUTH_TOKEN" http://crossbar:8000/v2/accounts/{ACCOUNT_ID}/notifications/voicemail_to_email | python -mjson.tool 9 | ``` 10 | 11 | ```json 12 | "data": {... 13 | "macros": { 14 | "call_id": { 15 | "description": "Call ID of the caller", 16 | "friendly_name": "Call ID", 17 | "i18n_label": "call_id" 18 | }, 19 | "callee_id.name": { 20 | "description": "Name of the callee", 21 | "friendly_name": "Callee ID Name", 22 | "i18n_label": "callee_id_name" 23 | }, 24 | "callee_id.number": { 25 | "description": "Number of the callee", 26 | "friendly_name": "Callee ID Number", 27 | "i18n_label": "callee_id_number" 28 | }, 29 | "caller_id.name": { 30 | "description": "Name of the caller", 31 | "friendly_name": "Caller ID Name", 32 | "i18n_label": "caller_id_name" 33 | }, 34 | "caller_id.number": { 35 | "description": "Number of the caller", 36 | "friendly_name": "Caller ID Number", 37 | "i18n_label": "caller_id_number" 38 | }, 39 | "date_called.local": { 40 | "description": "When was the voicemail left (Local time)", 41 | "friendly_name": "Date", 42 | "i18n_label": "date_called_local" 43 | }, 44 | "date_called.utc": { 45 | "description": "When was the voicemail left (UTC)", 46 | "friendly_name": "Date (UTC)", 47 | "i18n_label": "date_called_utc" 48 | }, 49 | "from.realm": { 50 | "description": "SIP From Realm", 51 | "friendly_name": "From Realm", 52 | "i18n_label": "from_realm" 53 | }, 54 | "from.user": { 55 | "description": "SIP From Username", 56 | "friendly_name": "From User", 57 | "i18n_label": "from_user" 58 | }, 59 | "owner.first_name": { 60 | "description": "First name of the owner of the voicemail box", 61 | "friendly_name": "First Name", 62 | "i18n_label": "first_name" 63 | }, 64 | "owner.last_name": { 65 | "description": "Last name of the owner of the voicemail box", 66 | "friendly_name": "Last Name", 67 | "i18n_label": "last_name" 68 | }, 69 | "to.realm": { 70 | "description": "SIP To Realm", 71 | "friendly_name": "To Realm", 72 | "i18n_label": "to_realm" 73 | }, 74 | "to.user": { 75 | "description": "SIP To Username", 76 | "friendly_name": "To User", 77 | "i18n_label": "to_user" 78 | }, 79 | "voicemail.box": { 80 | "description": "Which voicemail box was the message left in", 81 | "friendly_name": "Voicemail Box", 82 | "i18n_label": "voicemail_box" 83 | }, 84 | "voicemail.length": { 85 | "description": "Length of the voicemail file", 86 | "friendly_name": "Voicemail Length", 87 | "i18n_label": "voicemail_length" 88 | }, 89 | "voicemail.name": { 90 | "description": "Name of the voicemail file", 91 | "friendly_name": "Voicemail Name", 92 | "i18n_label": "voicemail_name" 93 | } 94 | } 95 | ,... 96 | } 97 | ``` 98 | 99 | You can read more [here](https://github.com/2600hz/kazoo/blob/master/applications/crossbar/doc/notifications.md). 100 | -------------------------------------------------------------------------------- /fix_docs.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function comment { 4 | echo comment out $1 5 | sed -i "\|$1| s|^|# |" doc/mkdocs/mkdocs.yml 6 | } 7 | 8 | for file in `find doc/ -size -1024c`; do 9 | comment $file 10 | done 11 | -------------------------------------------------------------------------------- /make/splchk.mk: -------------------------------------------------------------------------------- 1 | .PHONY = splchk splchk-changed splchk-json splchk-code 2 | 3 | KAZOO_DICT = .aspell.en.pws 4 | KAZOO_REPL = .aspell.en.prepl 5 | 6 | $(ROOT)/$(KAZOO_DICT): 7 | @$(file >$(ROOT)/$(KAZOO_DICT),personal_ws-1.1 en 0 utf-8) 8 | 9 | $(ROOT)/$(KAZOO_REPL): 10 | @$(file >$(ROOT)/$(KAZOO_REPL),personal_repl-1.1 en 0 utf-8) 11 | 12 | splchk-init: $(ROOT)/$(KAZOO_DICT) $(ROOT)/$(KAZOO_REPL) 13 | 14 | ifeq ($(wildcard $(CURDIR)/doc/*.md),) 15 | splchk: splchk-init 16 | else 17 | DOCS := $(shell find $(CURDIR)/doc -type f -not -path "doc/mkdocs/*" -name "*.md") 18 | splchk: splchk-init $(addsuffix .chk,$(basename $(DOCS))) 19 | endif 20 | 21 | JSON := $(wildcard $(CURDIR)/priv/couchdb/schemas/*.json) 22 | ifeq ($(JSON),) 23 | splchk-json: splchk-init 24 | else 25 | splchk-json: splchk-init $(addsuffix .chk,$(basename $(JSON))) 26 | endif 27 | 28 | ESCRIPTS := $(wildcard $(CURDIR)/scripts/*.escript) 29 | SRC := $(wildcard $(CURDIR)/src/*.*rl) $(wildcard $(CURDIR)/src/*/*.erl) $(wildcard $(CURDIR)/include/*.hrl) 30 | CODE := $(SRC) $(ESCRIPTS) 31 | ifeq ($(CODE),) 32 | splchk-code: splchk-init 33 | else 34 | splchk-code: splchk-init $(addsuffix .chk,$(basename $(CODE))) 35 | endif 36 | 37 | splchk-changed: splchk-init $(addsuffix .chk,$(basename $(CHANGED))) 38 | 39 | %.chk: %.md 40 | @aspell --home-dir=$(ROOT) --personal=$(KAZOO_DICT) --repl=$(KAZOO_REPL) --lang=en -x check $< 41 | 42 | %.chk: %.json 43 | @aspell --home-dir=$(ROOT) --personal=$(KAZOO_DICT) --repl=$(KAZOO_REPL) --lang=en -x check $< 44 | 45 | %.chk: %.erl 46 | @aspell --add-filter-path=$(ROOT) --mode=erlang --home-dir=$(ROOT) --personal=$(KAZOO_DICT) --repl=$(KAZOO_REPL) --lang=en -x check $< 47 | 48 | %.chk: %.escript 49 | @aspell --add-filter-path=$(ROOT) --mode=erlang --home-dir=$(ROOT) --personal=$(KAZOO_DICT) --repl=$(KAZOO_REPL) --lang=en -x check $< 50 | 51 | %.chk: %.hrl 52 | @aspell --add-filter-path=$(ROOT) --mode=erlang --home-dir=$(ROOT) --personal=$(KAZOO_DICT) --repl=$(KAZOO_REPL) --lang=en -x check $< 53 | -------------------------------------------------------------------------------- /scripts/reconcile_docs_to_index.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd $(dirname $0) > /dev/null 4 | 5 | cd $(pwd -P)/.. 6 | 7 | doc_count=0 8 | missing_count=0 9 | 10 | function check_index { 11 | line=$(grep "$1" ./doc/mkdocs/mkdocs.yml | grep -v "#") 12 | 13 | if [ -f "$1" ] && [ -z "$line" ]; then 14 | [[ 0 -eq $missing_count ]] && echo "Docs missing from the mkdocs.yml index:" 15 | ((missing_count+=1)) 16 | echo "$missing_count: '$1'" 17 | fi 18 | } 19 | 20 | docs=$(find {scripts,doc} \( -path 'doc/mkdocs' -o -path 'applications/*/doc/ref' \) -prune -o -type f -regex ".+\.md$") 21 | for doc in $docs; do 22 | ((doc_count+=1)) 23 | check_index $doc 24 | done 25 | 26 | # if [[ $missing ]]; then 27 | ratio=$((100 * $missing_count / $doc_count)) 28 | echo "Missing $missing_count / $doc_count: $ratio%" 29 | # fi 30 | 31 | popd > /dev/null 32 | -------------------------------------------------------------------------------- /scripts/setup_docs.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## sets up the docs environment to serve mkdocs-built site 3 | 4 | pushd $(dirname $0) > /dev/null 5 | 6 | cd $(pwd -P)/.. 7 | DOCS_ROOT=./doc/mkdocs/docs 8 | 9 | find doc/ -type f -path "doc/mkdocs*" -prune -o -regex ".+\.\(md\|png\|jpg\|svg\)$" -print | cpio -p -dum --quiet $DOCS_ROOT 10 | 11 | cp $DOCS_ROOT/../index.md $DOCS_ROOT 12 | 13 | popd > /dev/null 14 | -------------------------------------------------------------------------------- /scripts/validate_mkdocs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import yaml, os.path, sys 4 | from functools import reduce 5 | 6 | # from https://stackoverflow.com/questions/5574702/how-to-print-to-stderr-in-python 7 | def eprint(*args, **kwargs): 8 | print(*args, file=sys.stderr, **kwargs) 9 | 10 | def parse_page_dict(errors_detected, kv): 11 | (header, pages) = kv 12 | if pages is None: 13 | eprint("section ", header, " is incomplete") 14 | return True 15 | else: 16 | return parse_page(errors_detected, pages) 17 | 18 | def parse_page_string(errors_detected, page): 19 | if "index.md" != page and (not os.path.isfile(page)): 20 | eprint("page ", page, " is not valid") 21 | return True 22 | else: 23 | return errors_detected 24 | 25 | def parse_page(errors_detected, page): 26 | "parse a page for existence" 27 | if isinstance(page, dict): 28 | return reduce(parse_page_dict, list(page.items()), errors_detected) 29 | elif isinstance(page, list): 30 | return reduce(parse_page, page, errors_detected) 31 | elif isinstance(page, str): 32 | return parse_page_string(errors_detected, page) 33 | else: 34 | return errors_detected 35 | 36 | stream = open("doc/mkdocs/mkdocs.yml", 'r') 37 | mkdocs = yaml.safe_load_all(stream) 38 | errors_detected = False 39 | 40 | for doc in mkdocs: 41 | for k,v in list(doc.items()): 42 | if "pages" == k: 43 | errors_detected = parse_page(False, v) 44 | 45 | if errors_detected: 46 | sys.exit(1) 47 | -------------------------------------------------------------------------------- /wiki/Confluence-space-export-153629.html.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Confluence-space-export-153629.html.zip -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/13926531/14155782.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/13926531/14155782.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/13926531/14155784.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/13926531/14155784.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/13926531/14155786.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/13926531/14155786.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/17269210/17694735.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/17269210/17694735.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/22020107/22216705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/22020107/22216705.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194375/17694722.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194375/17694722.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194375/17694724.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194375/17694724.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515905.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515905.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515906.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515906.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515907.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515907.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515908.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515908.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515909.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515909.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515910.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515910.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515911.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515911.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515912.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515912.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515913.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515913.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/4194509/43515914.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/4194509/43515914.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6488074/6520834.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6488074/6520834.jpg -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/151650308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/151650308.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/30015572.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/30015572.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/66453507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/66453507.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/66453509.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/66453509.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/66453511.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/66453511.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/7208963.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/7208963.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/7208965.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/7208965.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/7208967.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/7208967.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/7208969.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/7208969.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/6979633/7634946.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/6979633/7634946.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504053/7634948.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504053/7634948.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504053/7634950.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504053/7634950.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504155/7634951.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504155/7634951.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504155/7634952.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504155/7634952.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504155/7634953.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504155/7634953.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504155/7634954.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504155/7634954.png -------------------------------------------------------------------------------- /wiki/Dedicated/attachments/7504155/7634955.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/attachments/7504155/7634955.png -------------------------------------------------------------------------------- /wiki/Dedicated/images/icons/bullet_blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/images/icons/bullet_blue.gif -------------------------------------------------------------------------------- /wiki/Dedicated/images/icons/contenttypes/home_page_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/images/icons/contenttypes/home_page_16.png -------------------------------------------------------------------------------- /wiki/Dedicated/images/icons/grey_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2600hz/docs-sysadmin/17fc48b5316f5f88b8bb97d1506d126e78b7b7c3/wiki/Dedicated/images/icons/grey_arrow_down.png -------------------------------------------------------------------------------- /wiki/Dedicated/service-plans/intro.md: -------------------------------------------------------------------------------- 1 | ######**Service Plans** 2 | 3 | Service plans allow you to define pricing packages based on features utilized by a customer or reseller. 4 |   5 |   6 | ######**Service Limits** 7 | 8 | Service limits enforce real-time limits and quotas for individual accounts. Allow flat-rate calling to select areas or limit international calling. 9 |   10 |   11 | ######**Call Rating** 12 | 13 | Load a basic rate-deck into the system to allow for international calling and per-minute services, real-time. Compare your retail costs with actual costs. 14 | -------------------------------------------------------------------------------- /wiki/parse_wiki.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | ## Usage: 3 | ## for file in `find . -name *.html`; do ./parse_wiki.py $file; done 4 | 5 | import sys, os, re 6 | from HTMLParser import HTMLParser 7 | 8 | class WikiParser(HTMLParser): 9 | def __init__(self): 10 | HTMLParser.__init__(self) 11 | self.recording = 0 12 | self.data = [] 13 | 14 | def handle_starttag(self, tag, attributes): 15 | if tag != 'div': 16 | return 17 | if self.recording: 18 | self.recording += 1 19 | return 20 | for name, value in attributes: 21 | if name == 'id' and value == 'main-content': 22 | break 23 | else: 24 | return 25 | self.recording = 1 26 | 27 | def handle_endtag(self, tag): 28 | if tag == 'div' and self.recording: 29 | self.recording -= 1 30 | 31 | def handle_data(self, data): 32 | if self.recording: 33 | self.data.append(data) 34 | 35 | html = open(sys.argv[1], 'r') 36 | wp = WikiParser() 37 | 38 | wp.feed(html.read()) 39 | md = open(os.path.splitext(sys.argv[1])[0] + ".md", 'w') 40 | 41 | for line in wp.data: 42 | if not re.match('^\s+$', line): 43 | md.write("%s\n" % line) 44 | 45 | wp.close() 46 | md.close() 47 | html.close() 48 | 49 | os.remove(sys.argv[1]) 50 | --------------------------------------------------------------------------------